@optifye/dashboard-core 6.11.42 → 6.11.44

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
@@ -13458,10 +13458,19 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
13458
13458
 
13459
13459
  // src/lib/hooks/useDashboardMetrics.ts
13460
13460
  var DEBUG_DASHBOARD_LOGS = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
13461
+ var REALTIME_REFRESH_DEBOUNCE_MS = 1500;
13462
+ var REALTIME_REFRESH_MIN_INTERVAL_MS = 5e3;
13461
13463
  var logDebug = (...args) => {
13462
13464
  if (!DEBUG_DASHBOARD_LOGS) return;
13463
13465
  console.log(...args);
13464
13466
  };
13467
+ var buildMetricsScopeKey = (lineId, lineIds) => {
13468
+ const normalizedLineIds = Array.from(new Set((lineIds || []).filter(Boolean))).sort();
13469
+ if (normalizedLineIds.length > 0) {
13470
+ return `${lineId}|${normalizedLineIds.join(",")}`;
13471
+ }
13472
+ return lineId;
13473
+ };
13465
13474
  var parseEfficiencyLegend = (legend) => {
13466
13475
  if (!legend) return null;
13467
13476
  const coerce = (value, fallback) => {
@@ -13541,6 +13550,7 @@ var useDashboardMetrics = ({
13541
13550
  const supabase = useSupabase();
13542
13551
  const [metrics2, setMetrics] = useState({ workspaceMetrics: [], lineMetrics: [] });
13543
13552
  const [metricsLineId, setMetricsLineId] = useState(lineId ?? null);
13553
+ const [metricsScopeKey, setMetricsScopeKey] = useState(() => buildMetricsScopeKey(lineId, lineIds));
13544
13554
  const [isLoading, setIsLoading] = useState(true);
13545
13555
  const [error, setError] = useState(null);
13546
13556
  const lineIdRef = useRef(lineId);
@@ -13550,8 +13560,10 @@ var useDashboardMetrics = ({
13550
13560
  const abortControllerRef = useRef(null);
13551
13561
  const lastFetchKeyRef = useRef(null);
13552
13562
  const inFlightFetchKeyRef = useRef(null);
13553
- const updateQueueRef = useRef(false);
13554
13563
  const onLineMetricsUpdateRef = useRef(onLineMetricsUpdate);
13564
+ const pendingRealtimeRefreshRef = useRef(false);
13565
+ const realtimeRefreshTimerRef = useRef(null);
13566
+ const lastRealtimeRefreshStartedAtRef = useRef(0);
13555
13567
  const shiftGroupsRef = useRef(shiftGroups);
13556
13568
  const operationalShiftKeyRef = useRef(operationalShiftKey);
13557
13569
  const configuredLineIdsRef = useRef(configuredLineIds);
@@ -13579,15 +13591,17 @@ var useDashboardMetrics = ({
13579
13591
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
13580
13592
  [entityConfig.companyId]
13581
13593
  );
13594
+ const requestedScopeKey = useMemo(
13595
+ () => buildMetricsScopeKey(lineId, isFactoryView ? targetFactoryLineIds : void 0),
13596
+ [isFactoryView, lineId, targetFactoryLineIds]
13597
+ );
13582
13598
  useEffect(() => {
13583
13599
  lineIdRef.current = lineId;
13584
- setMetrics({ workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND });
13585
- setMetricsLineId(null);
13586
13600
  setIsLoading(true);
13587
13601
  setError(null);
13588
13602
  lastFetchKeyRef.current = null;
13589
13603
  inFlightFetchKeyRef.current = null;
13590
- }, [lineId]);
13604
+ }, [lineId, requestedScopeKey]);
13591
13605
  const fetchAllMetrics = useCallback(async (options = {}) => {
13592
13606
  const { force = false } = options;
13593
13607
  const currentLineIdToUse = lineIdRef.current;
@@ -13600,7 +13614,6 @@ var useDashboardMetrics = ({
13600
13614
  companySpecificMetricsTable
13601
13615
  });
13602
13616
  if (!currentLineIdToUse || !supabase || !enabled || shiftLoading || isTimezoneLoading || companySpecificMetricsTable.includes("unknown_company")) {
13603
- updateQueueRef.current = false;
13604
13617
  if (!metrics2?.workspaceMetrics?.length && !metrics2?.lineMetrics?.length && !shiftLoading) setIsLoading(false);
13605
13618
  if (companySpecificMetricsTable.includes("unknown_company") && !error) {
13606
13619
  setError({ message: "Company ID not configured for metrics table.", code: "CONFIG_ERROR" });
@@ -13613,6 +13626,10 @@ var useDashboardMetrics = ({
13613
13626
  const usesShiftGroups = isFactory && shiftGroups.length > 0;
13614
13627
  const singleShiftDetails = usesShiftGroups ? null : shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(defaultTimezone, staticShiftConfig);
13615
13628
  const fetchKey = usesShiftGroups ? `factory|${companyId || "unknown"}|${shiftGroupsKey}` : isFactory ? `factory|${companyId || "unknown"}|${singleShiftDetails?.date}|${singleShiftDetails?.shiftId}|${targetLineIdsKey}` : `${currentLineIdToUse}|${companyId || "unknown"}|${singleShiftDetails?.date}|${singleShiftDetails?.shiftId}`;
13629
+ const responseScopeKey = buildMetricsScopeKey(
13630
+ currentLineIdToUse,
13631
+ isFactory ? targetLineIds : void 0
13632
+ );
13616
13633
  logDebug("[useDashboardMetrics] Fetch key details:", {
13617
13634
  isFactory,
13618
13635
  usesShiftGroups,
@@ -13622,14 +13639,8 @@ var useDashboardMetrics = ({
13622
13639
  singleShiftDetails,
13623
13640
  fetchKey
13624
13641
  });
13625
- if (inFlightFetchKeyRef.current === fetchKey) {
13626
- updateQueueRef.current = false;
13627
- return;
13628
- }
13629
- if (!force && lastFetchKeyRef.current === fetchKey) {
13630
- updateQueueRef.current = false;
13631
- return;
13632
- }
13642
+ if (inFlightFetchKeyRef.current === fetchKey) return;
13643
+ if (!force && lastFetchKeyRef.current === fetchKey) return;
13633
13644
  const requestId = ++activeRequestIdRef.current;
13634
13645
  const requestLineId = currentLineIdToUse;
13635
13646
  isFetchingRef.current = true;
@@ -13652,6 +13663,7 @@ var useDashboardMetrics = ({
13652
13663
  efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND
13653
13664
  });
13654
13665
  setMetricsLineId(requestLineId);
13666
+ setMetricsScopeKey(responseScopeKey);
13655
13667
  lastFetchKeyRef.current = inFlightFetchKeyRef.current;
13656
13668
  return;
13657
13669
  }
@@ -13861,6 +13873,7 @@ var useDashboardMetrics = ({
13861
13873
  hydrateFromBackend(idleTimeVlmByLine);
13862
13874
  setMetrics(newMetricsState);
13863
13875
  setMetricsLineId(requestLineId);
13876
+ setMetricsScopeKey(responseScopeKey);
13864
13877
  lastFetchKeyRef.current = inFlightFetchKeyRef.current;
13865
13878
  } catch (err) {
13866
13879
  if (abortController.signal.aborted || err?.name === "AbortError") {
@@ -13883,9 +13896,24 @@ var useDashboardMetrics = ({
13883
13896
  if (requestId === activeRequestIdRef.current) {
13884
13897
  setIsLoading(false);
13885
13898
  isFetchingRef.current = false;
13886
- updateQueueRef.current = false;
13887
13899
  activeFetchLineIdRef.current = null;
13888
13900
  inFlightFetchKeyRef.current = null;
13901
+ if (pendingRealtimeRefreshRef.current) {
13902
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
13903
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS - elapsedSinceLastRealtimeRefresh);
13904
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS, minIntervalRemaining);
13905
+ if (realtimeRefreshTimerRef.current === null) {
13906
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
13907
+ realtimeRefreshTimerRef.current = null;
13908
+ if (!pendingRealtimeRefreshRef.current || isFetchingRef.current || inFlightFetchKeyRef.current) {
13909
+ return;
13910
+ }
13911
+ pendingRealtimeRefreshRef.current = false;
13912
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
13913
+ fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13914
+ }, nextDelay);
13915
+ }
13916
+ }
13889
13917
  }
13890
13918
  }
13891
13919
  }, [
@@ -13915,20 +13943,59 @@ var useDashboardMetrics = ({
13915
13943
  useEffect(() => {
13916
13944
  fetchAllMetricsRef.current = fetchAllMetrics;
13917
13945
  }, [fetchAllMetrics]);
13918
- const queueUpdate = useCallback(() => {
13919
- if (updateQueueRef.current || !supabase) {
13920
- if (updateQueueRef.current) {
13921
- logDebug("[useDashboardMetrics] queueUpdate skipped: update already queued");
13946
+ const clearRealtimeRefreshTimer = useCallback(() => {
13947
+ if (realtimeRefreshTimerRef.current !== null) {
13948
+ window.clearTimeout(realtimeRefreshTimerRef.current);
13949
+ realtimeRefreshTimerRef.current = null;
13950
+ }
13951
+ }, []);
13952
+ const scheduleRealtimeRefresh = useCallback(() => {
13953
+ if (!enabled || !supabase) {
13954
+ pendingRealtimeRefreshRef.current = false;
13955
+ clearRealtimeRefreshTimer();
13956
+ return;
13957
+ }
13958
+ if (!pendingRealtimeRefreshRef.current || realtimeRefreshTimerRef.current !== null) {
13959
+ return;
13960
+ }
13961
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
13962
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS - elapsedSinceLastRealtimeRefresh);
13963
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS, minIntervalRemaining);
13964
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
13965
+ realtimeRefreshTimerRef.current = null;
13966
+ if (!pendingRealtimeRefreshRef.current) {
13967
+ return;
13922
13968
  }
13969
+ if (isFetchingRef.current || inFlightFetchKeyRef.current) {
13970
+ scheduleRealtimeRefresh();
13971
+ return;
13972
+ }
13973
+ pendingRealtimeRefreshRef.current = false;
13974
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
13975
+ fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13976
+ }, nextDelay);
13977
+ }, [clearRealtimeRefreshTimer, enabled, supabase]);
13978
+ useEffect(() => {
13979
+ return () => {
13980
+ clearRealtimeRefreshTimer();
13981
+ pendingRealtimeRefreshRef.current = false;
13982
+ };
13983
+ }, [clearRealtimeRefreshTimer]);
13984
+ const queueUpdate = useCallback(() => {
13985
+ if (!enabled) {
13986
+ logDebug("[useDashboardMetrics] queueUpdate skipped: metrics disabled");
13987
+ return;
13988
+ }
13989
+ if (!supabase) {
13923
13990
  if (!supabase) {
13924
13991
  logDebug("[useDashboardMetrics] queueUpdate skipped: supabase not ready");
13925
13992
  }
13926
13993
  return;
13927
13994
  }
13928
- logDebug("[useDashboardMetrics] queueUpdate triggered from realtime");
13929
- updateQueueRef.current = true;
13930
- fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13931
- }, [supabase, enabled]);
13995
+ pendingRealtimeRefreshRef.current = true;
13996
+ logDebug("[useDashboardMetrics] queueUpdate queued realtime refresh");
13997
+ scheduleRealtimeRefresh();
13998
+ }, [enabled, scheduleRealtimeRefresh, supabase]);
13932
13999
  useEffect(() => {
13933
14000
  if (enabled && lineId && supabase && !shiftLoading && !isTimezoneLoading) {
13934
14001
  fetchAllMetrics({ reason: "line-change" });
@@ -14048,7 +14115,7 @@ var useDashboardMetrics = ({
14048
14115
  { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
14049
14116
  (payload) => {
14050
14117
  const payloadData = payload.new || payload.old;
14051
- console.log("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
14118
+ logDebug("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
14052
14119
  eventType: payload.eventType,
14053
14120
  lineId: payloadData?.line_id,
14054
14121
  workspaceId: payloadData?.workspace_id,
@@ -14070,14 +14137,14 @@ var useDashboardMetrics = ({
14070
14137
  shiftId: payloadData?.shift_id
14071
14138
  });
14072
14139
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
14073
- console.log("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
14140
+ logDebug("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
14074
14141
  queueUpdate();
14075
14142
  } else {
14076
- console.log("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
14143
+ logDebug("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
14077
14144
  }
14078
14145
  }
14079
14146
  ).subscribe((status) => {
14080
- console.log("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
14147
+ logDebug("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
14081
14148
  channel: wsChannelName,
14082
14149
  status,
14083
14150
  table: companySpecificMetricsTable,
@@ -14095,7 +14162,7 @@ var useDashboardMetrics = ({
14095
14162
  { event: "*", schema, table: configuredLineMetricsTable, filter: filter2 },
14096
14163
  (payload) => {
14097
14164
  const payloadData = payload.new || payload.old;
14098
- console.log("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
14165
+ logDebug("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
14099
14166
  eventType: payload.eventType,
14100
14167
  lineId: payloadData?.line_id,
14101
14168
  date: payloadData?.date,
@@ -14115,15 +14182,15 @@ var useDashboardMetrics = ({
14115
14182
  shiftId: payloadData?.shift_id
14116
14183
  });
14117
14184
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
14118
- console.log("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
14185
+ logDebug("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
14119
14186
  queueUpdate();
14120
14187
  onLineMetricsUpdateRef.current?.();
14121
14188
  } else {
14122
- console.log("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
14189
+ logDebug("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
14123
14190
  }
14124
14191
  }
14125
14192
  ).subscribe((status) => {
14126
- console.log("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
14193
+ logDebug("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
14127
14194
  channel: lmChannelName,
14128
14195
  status,
14129
14196
  table: configuredLineMetricsTable,
@@ -14219,7 +14286,7 @@ var useDashboardMetrics = ({
14219
14286
  { event: "*", schema, table, filter: filter2 },
14220
14287
  (payload) => {
14221
14288
  const payloadData = payload.new || payload.old;
14222
- console.log(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
14289
+ logDebug(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
14223
14290
  eventType: payload.eventType,
14224
14291
  table: payload.table,
14225
14292
  lineId: payloadData?.line_id,
@@ -14243,14 +14310,14 @@ var useDashboardMetrics = ({
14243
14310
  shiftId: payloadData?.shift_id
14244
14311
  });
14245
14312
  if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
14246
- console.log(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
14313
+ logDebug(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
14247
14314
  callback();
14248
14315
  } else {
14249
- console.log(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
14316
+ logDebug(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
14250
14317
  }
14251
14318
  }
14252
14319
  ).subscribe((status) => {
14253
- console.log(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
14320
+ logDebug(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
14254
14321
  channel: channelName,
14255
14322
  status,
14256
14323
  table,
@@ -14299,14 +14366,17 @@ var useDashboardMetrics = ({
14299
14366
  lineId
14300
14367
  // NOTE: userAccessibleLineIds removed - accessed via ref
14301
14368
  ]);
14302
- const isMetricsForActiveLine = metricsLineId === lineId;
14303
- const safeMetrics = isMetricsForActiveLine ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
14369
+ const isCurrentScopeResolved = metricsScopeKey === requestedScopeKey;
14370
+ const hasLastGoodMetrics = metrics2.workspaceMetrics.length > 0 || metrics2.lineMetrics.length > 0;
14371
+ const canReuseLastGoodMetrics = hasLastGoodMetrics && !isCurrentScopeResolved && (isLoading || !!error);
14372
+ const safeMetrics = isCurrentScopeResolved || canReuseLastGoodMetrics ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
14304
14373
  return {
14305
14374
  workspaceMetrics: safeMetrics?.workspaceMetrics || [],
14306
14375
  lineMetrics: safeMetrics?.lineMetrics || [],
14307
14376
  efficiencyLegend: safeMetrics?.efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND,
14308
14377
  metadata: safeMetrics?.metadata,
14309
- isLoading: enabled ? isLoading || !isMetricsForActiveLine : false,
14378
+ isLoading: enabled ? isLoading || !isCurrentScopeResolved && !canReuseLastGoodMetrics : false,
14379
+ isCurrentScopeResolved,
14310
14380
  error,
14311
14381
  refetch: () => {
14312
14382
  if (!enabled) {
@@ -16754,6 +16824,7 @@ var isInitialized = false;
16754
16824
  var isInitializing = false;
16755
16825
  var initializedWithLineIds = [];
16756
16826
  var missingLineContextWarnings = /* @__PURE__ */ new Set();
16827
+ var lineLoadPromises = /* @__PURE__ */ new Map();
16757
16828
  var initializationPromise = null;
16758
16829
  var workspaceDisplayNamesListeners = /* @__PURE__ */ new Set();
16759
16830
  var notifyWorkspaceDisplayNamesListeners = (changedLineId) => {
@@ -16769,6 +16840,30 @@ var subscribeWorkspaceDisplayNames = (listener) => {
16769
16840
  workspaceDisplayNamesListeners.add(listener);
16770
16841
  return () => workspaceDisplayNamesListeners.delete(listener);
16771
16842
  };
16843
+ var storeLineDisplayNames = (lineId, lineDisplayNamesMap) => {
16844
+ runtimeWorkspaceDisplayNames[lineId] = {};
16845
+ lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16846
+ runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16847
+ });
16848
+ };
16849
+ var ensureLineWorkspaceDisplayNamesLoaded = async (lineId) => {
16850
+ if (!lineId || runtimeWorkspaceDisplayNames[lineId]) {
16851
+ return;
16852
+ }
16853
+ const existingPromise = lineLoadPromises.get(lineId);
16854
+ if (existingPromise) {
16855
+ await existingPromise;
16856
+ return;
16857
+ }
16858
+ const loadPromise = workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16859
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
16860
+ notifyWorkspaceDisplayNamesListeners(lineId);
16861
+ }).finally(() => {
16862
+ lineLoadPromises.delete(lineId);
16863
+ });
16864
+ lineLoadPromises.set(lineId, loadPromise);
16865
+ await loadPromise;
16866
+ };
16772
16867
  var getAllWorkspaceDisplayNamesSnapshot = (lineId) => {
16773
16868
  if (lineId && runtimeWorkspaceDisplayNames[lineId]) {
16774
16869
  return { ...runtimeWorkspaceDisplayNames[lineId] };
@@ -16833,6 +16928,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
16833
16928
  }
16834
16929
  console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
16835
16930
  runtimeWorkspaceDisplayNames = {};
16931
+ lineLoadPromises.clear();
16836
16932
  if (targetLineIds.length > 0) {
16837
16933
  const results = await Promise.all(
16838
16934
  targetLineIds.map(async (lineId) => {
@@ -16842,10 +16938,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
16842
16938
  })
16843
16939
  );
16844
16940
  results.forEach(({ lineId, lineDisplayNamesMap }) => {
16845
- runtimeWorkspaceDisplayNames[lineId] = {};
16846
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16847
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16848
- });
16941
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
16849
16942
  console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16850
16943
  });
16851
16944
  } else {
@@ -16877,13 +16970,8 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
16877
16970
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16878
16971
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
16879
16972
  try {
16880
- const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
16881
- runtimeWorkspaceDisplayNames[lineId] = {};
16882
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16883
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16884
- });
16885
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16886
- notifyWorkspaceDisplayNamesListeners(lineId);
16973
+ await ensureLineWorkspaceDisplayNamesLoaded(lineId);
16974
+ console.log(`\u2705 Added workspaces for line ${lineId}`);
16887
16975
  } catch (error) {
16888
16976
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16889
16977
  }
@@ -16896,13 +16984,8 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
16896
16984
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16897
16985
  console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
16898
16986
  try {
16899
- const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
16900
- runtimeWorkspaceDisplayNames[lineId] = {};
16901
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16902
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16903
- });
16904
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16905
- notifyWorkspaceDisplayNamesListeners(lineId);
16987
+ await ensureLineWorkspaceDisplayNamesLoaded(lineId);
16988
+ console.log(`\u2705 Added workspaces for line ${lineId}`);
16906
16989
  } catch (error) {
16907
16990
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16908
16991
  }
@@ -16939,13 +17022,8 @@ var getWorkspaceDisplayName = (workspaceId, lineId) => {
16939
17022
  }
16940
17023
  if (isInitialized && lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16941
17024
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching its workspaces...`);
16942
- workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16943
- runtimeWorkspaceDisplayNames[lineId] = {};
16944
- lineDisplayNamesMap.forEach((displayName2, workspaceId2) => {
16945
- runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
16946
- });
16947
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
16948
- notifyWorkspaceDisplayNamesListeners(lineId);
17025
+ ensureLineWorkspaceDisplayNamesLoaded(lineId).then(() => {
17026
+ console.log(`\u2705 Added workspaces for line ${lineId} to cache`);
16949
17027
  }).catch((error) => {
16950
17028
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16951
17029
  });
@@ -16987,13 +17065,8 @@ var getShortWorkspaceDisplayName = (workspaceId, lineId) => {
16987
17065
  }
16988
17066
  if (isInitialized && lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16989
17067
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching its workspaces...`);
16990
- workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16991
- runtimeWorkspaceDisplayNames[lineId] = {};
16992
- lineDisplayNamesMap.forEach((displayName2, workspaceId2) => {
16993
- runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
16994
- });
16995
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
16996
- notifyWorkspaceDisplayNamesListeners(lineId);
17068
+ ensureLineWorkspaceDisplayNamesLoaded(lineId).then(() => {
17069
+ console.log(`\u2705 Added workspaces for line ${lineId} to cache`);
16997
17070
  }).catch((error) => {
16998
17071
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16999
17072
  });
@@ -17073,6 +17146,7 @@ var refreshWorkspaceDisplayNames = async (companyId) => {
17073
17146
  workspaceService.clearWorkspaceDisplayNamesCache();
17074
17147
  runtimeWorkspaceDisplayNames = {};
17075
17148
  isInitialized = false;
17149
+ lineLoadPromises.clear();
17076
17150
  await initializeWorkspaceDisplayNames();
17077
17151
  notifyWorkspaceDisplayNamesListeners();
17078
17152
  };
@@ -17082,6 +17156,7 @@ var clearWorkspaceDisplayNamesCache = () => {
17082
17156
  isInitialized = false;
17083
17157
  isInitializing = false;
17084
17158
  initializedWithLineIds = [];
17159
+ lineLoadPromises.clear();
17085
17160
  initializationPromise = null;
17086
17161
  notifyWorkspaceDisplayNamesListeners();
17087
17162
  };
@@ -17211,7 +17286,16 @@ var useWorkspaceDisplayNamesMap = (workspaceIds, lineId, companyId) => {
17211
17286
  refetch: fetchDisplayNames
17212
17287
  };
17213
17288
  };
17214
- var useWorkspaceVideoStreams = (workspaces) => {
17289
+ var useWorkspaceVideoStreams = (workspaces, options = {}) => {
17290
+ const explicitLineIdsKey = useMemo(() => {
17291
+ const ids = /* @__PURE__ */ new Set();
17292
+ for (const lineId of options.lineIds || []) {
17293
+ if (lineId) {
17294
+ ids.add(lineId);
17295
+ }
17296
+ }
17297
+ return Array.from(ids).sort().join(",");
17298
+ }, [options.lineIds]);
17215
17299
  const workspaceIdsKey = useMemo(() => {
17216
17300
  const ids = /* @__PURE__ */ new Set();
17217
17301
  for (const workspace of workspaces) {
@@ -17221,7 +17305,7 @@ var useWorkspaceVideoStreams = (workspaces) => {
17221
17305
  }
17222
17306
  return Array.from(ids).sort().join(",");
17223
17307
  }, [workspaces]);
17224
- const lineIdsKey = useMemo(() => {
17308
+ const inferredLineIdsKey = useMemo(() => {
17225
17309
  const ids = /* @__PURE__ */ new Set();
17226
17310
  for (const workspace of workspaces) {
17227
17311
  if (workspace.line_id) {
@@ -17230,18 +17314,19 @@ var useWorkspaceVideoStreams = (workspaces) => {
17230
17314
  }
17231
17315
  return Array.from(ids).sort().join(",");
17232
17316
  }, [workspaces]);
17317
+ const lineIdsKey = explicitLineIdsKey || inferredLineIdsKey;
17233
17318
  const requestKey = useMemo(() => {
17234
- if (workspaceIdsKey) {
17235
- return `workspaces:${workspaceIdsKey}`;
17236
- }
17237
17319
  if (lineIdsKey) {
17238
17320
  return `lines:${lineIdsKey}`;
17239
17321
  }
17322
+ if (workspaceIdsKey) {
17323
+ return `workspaces:${workspaceIdsKey}`;
17324
+ }
17240
17325
  return "";
17241
17326
  }, [workspaceIdsKey, lineIdsKey]);
17242
17327
  const workspaceIds = useMemo(
17243
- () => workspaceIdsKey ? workspaceIdsKey.split(",") : [],
17244
- [workspaceIdsKey]
17328
+ () => lineIdsKey ? [] : workspaceIdsKey ? workspaceIdsKey.split(",") : [],
17329
+ [lineIdsKey, workspaceIdsKey]
17245
17330
  );
17246
17331
  const lineIds = useMemo(
17247
17332
  () => lineIdsKey ? lineIdsKey.split(",") : [],
@@ -18785,6 +18870,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18785
18870
  const supabase = useSupabase();
18786
18871
  const databaseConfig = useDatabaseConfig();
18787
18872
  const refreshInterval = options.refreshInterval ?? DEFAULT_REFRESH_INTERVAL_MS;
18873
+ const enabled = options.enabled ?? true;
18788
18874
  const workspaceIdsKey = useMemo(() => {
18789
18875
  const ids = Array.from(new Set(workspaceIds.filter(Boolean)));
18790
18876
  return ids.sort().join(",");
@@ -18795,7 +18881,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18795
18881
  const isFetchingRef = useRef(false);
18796
18882
  const refreshIntervalRef = useRef(null);
18797
18883
  const fetchLastSeen = useCallback(async () => {
18798
- if (!supabase || !workspaceIdsKey || isFetchingRef.current) return;
18884
+ if (!enabled || !supabase || !workspaceIdsKey || isFetchingRef.current) return;
18799
18885
  const healthTable = databaseConfig?.tables?.workspace_health || "workspace_health_status";
18800
18886
  try {
18801
18887
  isFetchingRef.current = true;
@@ -18827,12 +18913,18 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18827
18913
  setIsLoading(false);
18828
18914
  isFetchingRef.current = false;
18829
18915
  }
18830
- }, [supabase, workspaceIdsKey, databaseConfig?.tables?.workspace_health]);
18916
+ }, [databaseConfig?.tables?.workspace_health, enabled, supabase, workspaceIdsKey]);
18831
18917
  useEffect(() => {
18918
+ if (!enabled) {
18919
+ setIsLoading(false);
18920
+ setError(null);
18921
+ setLastSeenByWorkspaceId({});
18922
+ return;
18923
+ }
18832
18924
  fetchLastSeen();
18833
- }, [fetchLastSeen]);
18925
+ }, [enabled, fetchLastSeen]);
18834
18926
  useEffect(() => {
18835
- if (!refreshInterval || !workspaceIdsKey) return;
18927
+ if (!enabled || !refreshInterval || !workspaceIdsKey) return;
18836
18928
  refreshIntervalRef.current = setInterval(() => {
18837
18929
  fetchLastSeen();
18838
18930
  }, refreshInterval);
@@ -18842,7 +18934,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18842
18934
  refreshIntervalRef.current = null;
18843
18935
  }
18844
18936
  };
18845
- }, [fetchLastSeen, refreshInterval, workspaceIdsKey]);
18937
+ }, [enabled, fetchLastSeen, refreshInterval, workspaceIdsKey]);
18846
18938
  return {
18847
18939
  lastSeenByWorkspaceId,
18848
18940
  isLoading,
@@ -34675,7 +34767,7 @@ var VideoCard = React143__default.memo(({
34675
34767
  });
34676
34768
  const showOffline = Boolean(isStreamStale);
34677
34769
  const lastSeenText = lastSeenLabel || "Unknown";
34678
- const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
34770
+ const workspaceDisplayName = displayName || workspace.displayName || workspace.workspace_name;
34679
34771
  const videoGridMetricValue = getVideoGridMetricValue(workspace);
34680
34772
  const videoGridDisplayValue = getVideoGridDisplayValue(workspace, effectiveLegend, displayMinuteBucket);
34681
34773
  const videoGridColorState = getVideoGridColorState(workspace, effectiveLegend);
@@ -34888,7 +34980,10 @@ var VideoGridView = React143__default.memo(({
34888
34980
  }
34889
34981
  return Array.from(ids);
34890
34982
  }, [workspaces]);
34891
- const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds);
34983
+ const healthFetchEnabled = !videoStreamsLoading && workspaces.length > 0;
34984
+ const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds, {
34985
+ enabled: healthFetchEnabled
34986
+ });
34892
34987
  useEffect(() => {
34893
34988
  const sample = workspaces.slice(0, 3).map((workspace) => ({
34894
34989
  id: workspace.workspace_uuid || workspace.workspace_name,
@@ -34987,6 +35082,9 @@ var VideoGridView = React143__default.memo(({
34987
35082
  }, [sortedWorkspaces, lineOrder, lineNames]);
34988
35083
  lineGroups.length > 1;
34989
35084
  const streamsReady = !videoStreamsLoading;
35085
+ const resolveWorkspaceDisplayName = useCallback((workspace) => {
35086
+ return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
35087
+ }, [displayNames]);
34990
35088
  const calculateOptimalGrid = useCallback(() => {
34991
35089
  if (!containerRef.current) return;
34992
35090
  const containerPadding = 16;
@@ -35117,11 +35215,11 @@ var VideoGridView = React143__default.memo(({
35117
35215
  efficiency: workspace.efficiency,
35118
35216
  action_count: workspace.action_count
35119
35217
  });
35120
- const displayName = workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
35218
+ const displayName = resolveWorkspaceDisplayName(workspace);
35121
35219
  const currentPath = (router.asPath || "/").split("#")[0];
35122
35220
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id, currentPath);
35123
35221
  router.push(`/workspace/${workspaceId}${navParams}`);
35124
- }, [router, prewarmClipsInit]);
35222
+ }, [resolveWorkspaceDisplayName, router, prewarmClipsInit]);
35125
35223
  const handleStreamError = useCallback((workspaceId, options) => {
35126
35224
  const isR2Stream = options?.isR2Stream ?? false;
35127
35225
  const hasFallback = Boolean(options?.fallbackUrl);
@@ -35230,7 +35328,7 @@ var VideoGridView = React143__default.memo(({
35230
35328
  legend: effectiveLegend,
35231
35329
  cropping: card.workspaceCropping,
35232
35330
  canvasFps: effectiveCanvasFps,
35233
- displayName: displayNames[`${card.workspace.line_id}_${card.workspace.workspace_name}`] || getWorkspaceDisplayName(card.workspace.workspace_name, card.workspace.line_id),
35331
+ displayName: resolveWorkspaceDisplayName(card.workspace),
35234
35332
  lastSeenLabel: card.lastSeenLabel,
35235
35333
  useRAF: effectiveUseRAF,
35236
35334
  displayMinuteBucket,
@@ -35242,16 +35340,15 @@ var VideoGridView = React143__default.memo(({
35242
35340
  },
35243
35341
  card.workspaceKey
35244
35342
  ), [
35245
- displayNames,
35246
35343
  displayMinuteBucket,
35247
35344
  effectiveCanvasFps,
35248
35345
  effectiveLegend,
35249
35346
  effectiveUseRAF,
35250
- getWorkspaceDisplayName,
35251
35347
  handleStreamError,
35252
35348
  handleWorkspaceClick,
35253
35349
  onWorkspaceHover,
35254
35350
  onWorkspaceHoverEnd,
35351
+ resolveWorkspaceDisplayName,
35255
35352
  selectedLine
35256
35353
  ]);
35257
35354
  return /* @__PURE__ */ jsx("div", { className: `relative overflow-hidden h-full w-full bg-slate-50/30 ${className}`, children: /* @__PURE__ */ jsx(
@@ -35337,6 +35434,9 @@ var MapGridView = React143__default.memo(({
35337
35434
  prewarmInFlightRef.current.delete(workspaceId);
35338
35435
  });
35339
35436
  }, [dashboardConfig, timezone, supabase]);
35437
+ const resolveWorkspaceDisplayName = useCallback((workspace) => {
35438
+ return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
35439
+ }, [displayNames]);
35340
35440
  const handleWorkspaceClick = useCallback((workspace) => {
35341
35441
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
35342
35442
  prewarmClipsInit(workspace);
@@ -35348,12 +35448,11 @@ var MapGridView = React143__default.memo(({
35348
35448
  efficiency: workspace.efficiency,
35349
35449
  action_count: workspace.action_count
35350
35450
  });
35351
- const displayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
35352
- getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
35451
+ const displayName = resolveWorkspaceDisplayName(workspace);
35353
35452
  const currentPath = (router.asPath || "/").split("#")[0];
35354
35453
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id, currentPath);
35355
35454
  router.push(`/workspace/${workspaceId}${navParams}`);
35356
- }, [router, displayNames, prewarmClipsInit]);
35455
+ }, [resolveWorkspaceDisplayName, router, prewarmClipsInit]);
35357
35456
  const activePositions = useMemo(() => {
35358
35457
  return workspacePositions.filter((pos) => {
35359
35458
  const wsKey = pos.id.toUpperCase();
@@ -35383,7 +35482,18 @@ var MapGridView = React143__default.memo(({
35383
35482
  workspace_uuid: position.id,
35384
35483
  // Use config ID as temporary UUID
35385
35484
  line_id: "",
35485
+ // Empty line ID
35486
+ company_id: "",
35487
+ shift_id: 0,
35488
+ date: "",
35386
35489
  efficiency: 0,
35490
+ performance_score: 0,
35491
+ action_count: 0,
35492
+ pph: 0,
35493
+ avg_cycle_time: 0,
35494
+ trend: 0,
35495
+ predicted_output: 0,
35496
+ action_threshold: 0,
35387
35497
  show_exclamation: false
35388
35498
  };
35389
35499
  const workspaceId = effectiveWorkspace.workspace_uuid || effectiveWorkspace.workspace_name;
@@ -35392,7 +35502,7 @@ var MapGridView = React143__default.memo(({
35392
35502
  const performanceColor = isInactivePlaceholder || isConveyorRow ? "bg-slate-200 border-slate-300 text-slate-500" : getPerformanceColor(effectiveWorkspace.efficiency);
35393
35503
  !isInactivePlaceholder && !isConveyorRow && (effectiveWorkspace.show_exclamation ?? (effectiveWorkspace.efficiency < 50 && effectiveWorkspace.efficiency >= 10));
35394
35504
  const configLabel = position.label;
35395
- const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : displayNames[`${effectiveWorkspace.line_id}_${effectiveWorkspace.workspace_name}`] || getWorkspaceDisplayName(effectiveWorkspace.workspace_name, effectiveWorkspace.line_id) || position.id);
35505
+ const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : resolveWorkspaceDisplayName(effectiveWorkspace) || position.id);
35396
35506
  return /* @__PURE__ */ jsx(
35397
35507
  motion.div,
35398
35508
  {
@@ -39359,9 +39469,12 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
39359
39469
  var LoadingOverlay = ({
39360
39470
  isVisible,
39361
39471
  message = "Loading...",
39362
- className
39472
+ className,
39473
+ contentClassName,
39474
+ contentVariant = "card"
39363
39475
  }) => {
39364
39476
  if (!isVisible) return null;
39477
+ const contentClasses = contentVariant === "plain" ? "flex flex-col items-center justify-center" : "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl";
39365
39478
  return /* @__PURE__ */ jsx(
39366
39479
  motion.div,
39367
39480
  {
@@ -39372,7 +39485,7 @@ var LoadingOverlay = ({
39372
39485
  className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
39373
39486
  "aria-modal": "true",
39374
39487
  role: "dialog",
39375
- children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message }) })
39488
+ children: /* @__PURE__ */ jsx("div", { className: `${contentClasses} ${contentClassName || ""}`, children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message }) })
39376
39489
  }
39377
39490
  );
39378
39491
  };
@@ -60702,6 +60815,353 @@ var HelpView = ({
60702
60815
  };
60703
60816
  var AuthenticatedHelpView = withAuth(HelpView);
60704
60817
  var HelpView_default = HelpView;
60818
+ var sortWorkspaceMetrics = (left, right) => {
60819
+ if (left.line_id !== right.line_id) {
60820
+ return left.line_id.localeCompare(right.line_id);
60821
+ }
60822
+ return left.workspace_name.localeCompare(right.workspace_name, void 0, { numeric: true });
60823
+ };
60824
+ var transformWorkspaceMetrics = (rows) => (rows || []).map((item) => {
60825
+ const actionFamily = normalizeActionFamily({
60826
+ actionFamily: item.action_family,
60827
+ actionType: item.action_type,
60828
+ actionName: item.action_name
60829
+ });
60830
+ const actionType = actionFamily === "assembly" || actionFamily === "output" ? actionFamily : null;
60831
+ const metric = {
60832
+ company_id: item.company_id || "",
60833
+ line_id: item.line_id,
60834
+ shift_id: item.shift_id,
60835
+ date: item.date,
60836
+ workspace_uuid: item.workspace_id,
60837
+ workspace_name: item.workspace_name,
60838
+ displayName: item.workspace_display_name || item.display_name || void 0,
60839
+ action_count: item.total_output || 0,
60840
+ pph: item.avg_pph || 0,
60841
+ performance_score: item.performance_score || 0,
60842
+ avg_cycle_time: item.avg_cycle_time || 0,
60843
+ ideal_cycle_time: item.ideal_cycle_time || void 0,
60844
+ trend: item.trend_score === 1 ? 2 : 0,
60845
+ predicted_output: item.ideal_output || 0,
60846
+ efficiency: item.efficiency || 0,
60847
+ action_threshold: item.total_day_output || 0,
60848
+ monitoring_mode: item.monitoring_mode ?? void 0,
60849
+ idle_time: typeof item.idle_time === "number" ? item.idle_time : Number(item.idle_time),
60850
+ idle_time_hourly: item.idle_time_hourly ?? null,
60851
+ shift_start: item.shift_start ?? void 0,
60852
+ shift_end: item.shift_end ?? void 0,
60853
+ assembly_enabled: item.assembly_enabled ?? false,
60854
+ video_grid_metric_mode: normalizeVideoGridMetricMode(
60855
+ item.video_grid_metric_mode,
60856
+ item.assembly_enabled ?? false
60857
+ ),
60858
+ action_type: actionType,
60859
+ action_family: actionFamily,
60860
+ action_display_name: getActionDisplayName({
60861
+ displayName: item.action_display_name,
60862
+ actionFamily: item.action_family,
60863
+ actionType: item.action_type,
60864
+ actionName: item.action_name
60865
+ }),
60866
+ recent_flow_percent: item.recent_flow_percent ?? null,
60867
+ recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
60868
+ recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
60869
+ recent_flow_computed_at: item.recent_flow_computed_at ?? null,
60870
+ scheduled_break_active: item.scheduled_break_active ?? false,
60871
+ incoming_wip_current: item.incoming_wip_current ?? null,
60872
+ incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
60873
+ incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null,
60874
+ show_exclamation: item.show_exclamation ?? void 0
60875
+ };
60876
+ workspaceMetricsStore.setOverview(metric);
60877
+ return metric;
60878
+ }).sort(sortWorkspaceMetrics);
60879
+ var transformActiveBreaks = (activeBreaksByLine) => {
60880
+ if (!activeBreaksByLine) return [];
60881
+ return Object.values(activeBreaksByLine).flat().map((item) => ({
60882
+ lineId: item.line_id,
60883
+ shiftName: item.shift_name || "",
60884
+ startTime: item.start_time,
60885
+ endTime: item.end_time,
60886
+ duration: item.duration || 0,
60887
+ remarks: item.remarks || "",
60888
+ elapsedMinutes: item.elapsed_minutes || 0,
60889
+ remainingMinutes: item.remaining_minutes || 0
60890
+ })).sort((left, right) => {
60891
+ if (left.lineId !== right.lineId) return left.lineId.localeCompare(right.lineId);
60892
+ if (left.shiftName !== right.shiftName) return left.shiftName.localeCompare(right.shiftName);
60893
+ return left.startTime.localeCompare(right.startTime);
60894
+ });
60895
+ };
60896
+ var createEmptyState = () => ({
60897
+ requestKey: null,
60898
+ resolvedScope: [],
60899
+ scopeKey: null,
60900
+ lines: [],
60901
+ workspaceMetrics: [],
60902
+ lineMetrics: [],
60903
+ kpiTrend: null,
60904
+ activeBreaks: [],
60905
+ videoStreamsByWorkspaceId: {},
60906
+ efficiencyLegend: null,
60907
+ metadata: {}
60908
+ });
60909
+ var normalizeMetadata = (metadata) => ({
60910
+ hasFlowBuffers: Boolean(metadata?.has_flow_buffers),
60911
+ idleTimeVlmByLine: metadata?.idle_time_vlm_by_line ?? {},
60912
+ generatedAt: metadata?.generated_at,
60913
+ cacheStatus: metadata?.cache_status,
60914
+ warnings: metadata?.warnings ?? []
60915
+ });
60916
+ var useLiveMonitorBootstrap = ({
60917
+ lineIds,
60918
+ companyId,
60919
+ enabled = true
60920
+ }) => {
60921
+ const supabase = useSupabase();
60922
+ const entityConfig = useEntityConfig();
60923
+ const { hydrateFromBackend } = useIdleTimeVlmConfig();
60924
+ const resolvedCompanyId = companyId || entityConfig?.companyId;
60925
+ const rawLineIdsKey = (lineIds || []).filter(Boolean).join(",");
60926
+ const normalizedLineIds = useMemo(
60927
+ () => Array.from(new Set(rawLineIdsKey ? rawLineIdsKey.split(",") : [])),
60928
+ [rawLineIdsKey]
60929
+ );
60930
+ const requestKey = useMemo(
60931
+ () => normalizedLineIds.slice().sort().join(","),
60932
+ [normalizedLineIds]
60933
+ );
60934
+ const [state, setState] = useState(() => createEmptyState());
60935
+ const [isLoading, setIsLoading] = useState(false);
60936
+ const [error, setError] = useState(null);
60937
+ const activeRequestIdRef = useRef(0);
60938
+ const fetchBootstrap = useCallback(async (force = false) => {
60939
+ if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
60940
+ return;
60941
+ }
60942
+ const requestId = ++activeRequestIdRef.current;
60943
+ setIsLoading(true);
60944
+ setError(null);
60945
+ try {
60946
+ const searchParams = new URLSearchParams();
60947
+ searchParams.set("company_id", resolvedCompanyId);
60948
+ searchParams.set("line_ids", normalizedLineIds.join(","));
60949
+ if (force) {
60950
+ searchParams.set("force_refresh", "true");
60951
+ }
60952
+ const response = await fetchBackendJson(
60953
+ supabase,
60954
+ `/api/dashboard/monitor-bootstrap?${searchParams.toString()}`,
60955
+ {
60956
+ method: "GET",
60957
+ timeoutMs: 2e4,
60958
+ retries: 0,
60959
+ dedupeKey: `monitor-bootstrap::${requestKey}::${force ? "force" : "cached"}`
60960
+ }
60961
+ );
60962
+ if (requestId !== activeRequestIdRef.current) {
60963
+ return;
60964
+ }
60965
+ const workspaceMetrics = transformWorkspaceMetrics(response.workspace_metrics || []);
60966
+ const activeBreaks = transformActiveBreaks(response.active_breaks_by_line);
60967
+ const metadata = normalizeMetadata(response.metadata);
60968
+ if (metadata.idleTimeVlmByLine) {
60969
+ hydrateFromBackend(metadata.idleTimeVlmByLine);
60970
+ }
60971
+ setState({
60972
+ requestKey,
60973
+ resolvedScope: response.resolved_scope || [],
60974
+ scopeKey: response.scope_key || null,
60975
+ lines: response.lines || [],
60976
+ workspaceMetrics,
60977
+ lineMetrics: response.line_metrics || [],
60978
+ kpiTrend: response.kpi_trend || null,
60979
+ activeBreaks,
60980
+ videoStreamsByWorkspaceId: response.video_streams_by_workspace_id || {},
60981
+ efficiencyLegend: response.efficiency_legend || null,
60982
+ metadata
60983
+ });
60984
+ } catch (fetchError) {
60985
+ if (requestId !== activeRequestIdRef.current) {
60986
+ return;
60987
+ }
60988
+ setError(fetchError instanceof Error ? fetchError : new Error("Failed to load live monitor bootstrap"));
60989
+ } finally {
60990
+ if (requestId === activeRequestIdRef.current) {
60991
+ setIsLoading(false);
60992
+ }
60993
+ }
60994
+ }, [enabled, supabase, resolvedCompanyId, normalizedLineIds, requestKey, hydrateFromBackend]);
60995
+ useEffect(() => {
60996
+ if (!enabled) {
60997
+ setIsLoading(false);
60998
+ return;
60999
+ }
61000
+ if (!resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
61001
+ setState(createEmptyState());
61002
+ setIsLoading(false);
61003
+ setError(null);
61004
+ return;
61005
+ }
61006
+ void fetchBootstrap(false);
61007
+ }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
61008
+ useEffect(() => {
61009
+ if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
61010
+ return void 0;
61011
+ }
61012
+ let intervalId = null;
61013
+ let timeoutId = null;
61014
+ const runMinuteTick = () => {
61015
+ void fetchBootstrap(true);
61016
+ };
61017
+ const startInterval = () => {
61018
+ runMinuteTick();
61019
+ intervalId = window.setInterval(runMinuteTick, 6e4);
61020
+ };
61021
+ const msUntilNextMinute = (() => {
61022
+ const remainder = Date.now() % 6e4;
61023
+ return remainder === 0 ? 6e4 : 6e4 - remainder;
61024
+ })();
61025
+ timeoutId = window.setTimeout(startInterval, msUntilNextMinute);
61026
+ return () => {
61027
+ if (timeoutId !== null) {
61028
+ window.clearTimeout(timeoutId);
61029
+ }
61030
+ if (intervalId !== null) {
61031
+ window.clearInterval(intervalId);
61032
+ }
61033
+ };
61034
+ }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
61035
+ const isCurrentScopeResolved = state.requestKey === requestKey;
61036
+ return {
61037
+ resolvedScope: state.resolvedScope,
61038
+ scopeKey: state.scopeKey,
61039
+ lines: state.lines,
61040
+ workspaceMetrics: state.workspaceMetrics,
61041
+ lineMetrics: state.lineMetrics,
61042
+ kpiTrend: state.kpiTrend,
61043
+ activeBreaks: state.activeBreaks,
61044
+ videoStreamsByWorkspaceId: state.videoStreamsByWorkspaceId,
61045
+ efficiencyLegend: state.efficiencyLegend,
61046
+ metadata: state.metadata,
61047
+ isLoading: enabled ? isLoading || !isCurrentScopeResolved : false,
61048
+ isCurrentScopeResolved,
61049
+ error,
61050
+ refetch: () => fetchBootstrap(true)
61051
+ };
61052
+ };
61053
+
61054
+ // src/lib/utils/liveMonitorBootstrap.ts
61055
+ var toNumber3 = (value) => {
61056
+ if (typeof value === "number" && Number.isFinite(value)) return value;
61057
+ if (typeof value === "string" && value.trim() !== "") {
61058
+ const parsed = Number(value);
61059
+ return Number.isFinite(parsed) ? parsed : 0;
61060
+ }
61061
+ return 0;
61062
+ };
61063
+ var normalizeNullableNumber = (value) => {
61064
+ if (value === null || value === void 0 || value === "") return null;
61065
+ return toNumber3(value);
61066
+ };
61067
+ var getLiveMonitorBootstrapMode = () => {
61068
+ const rawMode = (process.env.NEXT_PUBLIC_MONITOR_BOOTSTRAP_MODE || "").trim().toLowerCase();
61069
+ if (rawMode === "legacy" || rawMode === "shadow" || rawMode === "bootstrap") {
61070
+ return rawMode;
61071
+ }
61072
+ return "bootstrap";
61073
+ };
61074
+ var normalizeMonitorShadowSnapshot = (input) => {
61075
+ const sortedSelectedLineIds = Array.from(new Set((input.selectedLineIds || []).filter(Boolean))).sort();
61076
+ const resolvedScope = (input.resolvedScope || []).filter((entry) => entry?.line_id && entry?.date && entry?.shift_id !== void 0 && entry?.shift_id !== null).map((entry) => ({
61077
+ line_id: entry.line_id,
61078
+ date: entry.date,
61079
+ shift_id: entry.shift_id
61080
+ })).sort((left, right) => {
61081
+ if (left.line_id !== right.line_id) return left.line_id.localeCompare(right.line_id);
61082
+ if (left.date !== right.date) return left.date.localeCompare(right.date);
61083
+ return left.shift_id - right.shift_id;
61084
+ });
61085
+ const lines = (input.lines || []).filter((entry) => entry?.line_id).map((entry) => ({
61086
+ line_id: entry.line_id,
61087
+ line_name: entry.line_name || "Unknown Line"
61088
+ })).sort((left, right) => left.line_id.localeCompare(right.line_id));
61089
+ const workspaces = (input.workspaces || []).map((workspace) => {
61090
+ const workspaceId = workspace.workspace_uuid || null;
61091
+ const stream = workspaceId ? input.videoStreamsByWorkspaceId?.[workspaceId] || null : null;
61092
+ return {
61093
+ workspace_id: workspaceId,
61094
+ line_id: workspace.line_id,
61095
+ workspace_name: workspace.workspace_name,
61096
+ display_name: workspace.displayName || null,
61097
+ efficiency: toNumber3(workspace.efficiency),
61098
+ trend: toNumber3(workspace.trend),
61099
+ show_exclamation: Boolean(workspace.show_exclamation),
61100
+ scheduled_break_active: Boolean(workspace.scheduled_break_active),
61101
+ action_count: toNumber3(workspace.action_count),
61102
+ action_threshold: toNumber3(workspace.action_threshold),
61103
+ predicted_output: toNumber3(workspace.predicted_output),
61104
+ avg_cycle_time: toNumber3(workspace.avg_cycle_time),
61105
+ pph: toNumber3(workspace.pph),
61106
+ recent_flow_percent: normalizeNullableNumber(workspace.recent_flow_percent),
61107
+ incoming_wip_current: normalizeNullableNumber(workspace.incoming_wip_current),
61108
+ video_stream: stream ? {
61109
+ workspace_id: stream.workspace_id,
61110
+ camera_uuid: stream.camera_uuid,
61111
+ stream_camera_uuid: stream.stream_camera_uuid,
61112
+ hls_url: stream.hls_url,
61113
+ crop: stream.crop
61114
+ } : null
61115
+ };
61116
+ }).sort((left, right) => {
61117
+ if (left.line_id !== right.line_id) return left.line_id.localeCompare(right.line_id);
61118
+ return left.workspace_name.localeCompare(right.workspace_name, void 0, { numeric: true });
61119
+ });
61120
+ const activeBreaks = (input.activeBreaks || []).map((activeBreak) => ({
61121
+ lineId: activeBreak.lineId,
61122
+ shiftName: activeBreak.shiftName,
61123
+ startTime: activeBreak.startTime,
61124
+ endTime: activeBreak.endTime,
61125
+ remainingMinutes: toNumber3(activeBreak.remainingMinutes)
61126
+ })).sort((left, right) => {
61127
+ if (left.lineId !== right.lineId) return left.lineId.localeCompare(right.lineId);
61128
+ if (left.shiftName !== right.shiftName) return left.shiftName.localeCompare(right.shiftName);
61129
+ return left.startTime.localeCompare(right.startTime);
61130
+ });
61131
+ return {
61132
+ selectedLineIds: sortedSelectedLineIds,
61133
+ resolvedScope,
61134
+ lines,
61135
+ workspaces,
61136
+ activeBreaks,
61137
+ kpis: input.kpis || null,
61138
+ kpiTrend: input.kpiTrend || null,
61139
+ efficiencyLegend: input.efficiencyLegend || null
61140
+ };
61141
+ };
61142
+ var diffMonitorShadowSnapshots = (legacySnapshot, bootstrapSnapshot) => {
61143
+ const mismatches = [];
61144
+ const compareSection = (section) => {
61145
+ const legacyValue = legacySnapshot[section];
61146
+ const bootstrapValue = bootstrapSnapshot[section];
61147
+ if (JSON.stringify(legacyValue) !== JSON.stringify(bootstrapValue)) {
61148
+ mismatches.push({
61149
+ section,
61150
+ legacy: legacyValue,
61151
+ bootstrap: bootstrapValue
61152
+ });
61153
+ }
61154
+ };
61155
+ compareSection("selectedLineIds");
61156
+ compareSection("resolvedScope");
61157
+ compareSection("lines");
61158
+ compareSection("workspaces");
61159
+ compareSection("activeBreaks");
61160
+ compareSection("kpis");
61161
+ compareSection("kpiTrend");
61162
+ compareSection("efficiencyLegend");
61163
+ return mismatches;
61164
+ };
60705
61165
 
60706
61166
  // src/lib/services/notificationService.ts
60707
61167
  var API_BASE_URL = process.env.NEXT_PUBLIC_BACKEND_URL;
@@ -60843,7 +61303,10 @@ var logDebug3 = (...args) => {
60843
61303
  if (!DEBUG_DASHBOARD_LOGS3) return;
60844
61304
  console.log(...args);
60845
61305
  };
61306
+ var EMPTY_LINE_IDS = [];
61307
+ var EMPTY_WORKSPACES = [];
60846
61308
  var LoadingPageCmp = LoadingPage_default;
61309
+ var LoadingOverlayCmp = LoadingOverlay_default;
60847
61310
  function HomeView({
60848
61311
  defaultLineId,
60849
61312
  factoryViewId,
@@ -60991,6 +61454,7 @@ function HomeView({
60991
61454
  return createNotificationService(supabaseClient);
60992
61455
  }, [supabaseClient]);
60993
61456
  const [bottleneckNotification, setBottleneckNotification] = useState(null);
61457
+ const showBottleneckNotificationRef = useRef(null);
60994
61458
  const [bottleneckModalOpen, setBottleneckModalOpen] = useState(false);
60995
61459
  const [bottleneckModalData, setBottleneckModalData] = useState(null);
60996
61460
  const [diagnosisModalOpen, setDiagnosisModalOpen] = useState(false);
@@ -61001,6 +61465,11 @@ function HomeView({
61001
61465
  dashboardConfig?.shiftConfig
61002
61466
  );
61003
61467
  const shouldEnableMetricsFetch = authStatus === "ready";
61468
+ const liveMonitorMode = useMemo(() => getLiveMonitorBootstrapMode(), []);
61469
+ const isLegacyMonitorMode = liveMonitorMode === "legacy";
61470
+ const isShadowMonitorMode = liveMonitorMode === "shadow";
61471
+ const isBootstrapMonitorMode = liveMonitorMode === "bootstrap";
61472
+ const lastShadowMismatchSignatureRef = useRef(null);
61004
61473
  const handleLineMetricsUpdate = useCallback(() => {
61005
61474
  if (trendRefreshTimerRef.current) {
61006
61475
  window.clearTimeout(trendRefreshTimerRef.current);
@@ -61016,45 +61485,24 @@ function HomeView({
61016
61485
  }
61017
61486
  }, []);
61018
61487
  const {
61019
- workspaceMetrics,
61020
- lineMetrics,
61021
- efficiencyLegend,
61022
- metadata: metricsMetadata,
61023
- isLoading: metricsLoading,
61024
- error: metricsError,
61025
- refetch: refetchMetrics
61488
+ workspaceMetrics: legacyWorkspaceMetrics,
61489
+ lineMetrics: legacyLineMetrics,
61490
+ efficiencyLegend: legacyEfficiencyLegend,
61491
+ metadata: legacyMetricsMetadata,
61492
+ isLoading: legacyMetricsLoading,
61493
+ isCurrentScopeResolved: legacyIsCurrentScopeResolved,
61494
+ error: legacyMetricsError,
61495
+ refetch: refetchLegacyMetrics
61026
61496
  } = useDashboardMetrics({
61027
61497
  lineId: metricsScopeLineId,
61028
61498
  lineIds: selectedLineIds,
61029
61499
  onLineMetricsUpdate: handleLineMetricsUpdate,
61030
61500
  userAccessibleLineIds: visibleLineIds,
61031
- // Pass user's accessible lines for supervisor filtering
61032
- enabled: shouldEnableMetricsFetch
61501
+ enabled: shouldEnableMetricsFetch && !isBootstrapMonitorMode
61033
61502
  });
61034
- const metricsDisplayNames = useMemo(() => {
61035
- const nextDisplayNames = {};
61036
- workspaceMetrics.forEach((workspace) => {
61037
- if (!workspace.displayName) {
61038
- return;
61039
- }
61040
- nextDisplayNames[`${workspace.line_id}_${workspace.workspace_name}`] = workspace.displayName;
61041
- });
61042
- return nextDisplayNames;
61043
- }, [workspaceMetrics]);
61044
- useEffect(() => {
61045
- workspaceMetrics.forEach((workspace) => {
61046
- if (!workspace.displayName) {
61047
- return;
61048
- }
61049
- upsertWorkspaceDisplayNameInCache({
61050
- lineId: workspace.line_id,
61051
- workspaceId: workspace.workspace_name,
61052
- displayName: workspace.displayName
61053
- });
61054
- });
61055
- }, [workspaceMetrics]);
61056
- const trendGroups = useMemo(() => {
61057
- const lineMetricsRows = lineMetrics || [];
61503
+ const legacyTrendGroups = useMemo(() => {
61504
+ if (isBootstrapMonitorMode) return null;
61505
+ const lineMetricsRows = legacyLineMetrics || [];
61058
61506
  if (!lineMetricsRows.length) return null;
61059
61507
  if (selectedLineIds.length > 1) {
61060
61508
  const rowsForLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
@@ -61080,7 +61528,7 @@ function HomeView({
61080
61528
  shiftId: group.shiftId
61081
61529
  }));
61082
61530
  }
61083
- const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
61531
+ const row = lineMetricsRows.find((candidate) => candidate?.line_id === primarySelectedLineId);
61084
61532
  if (!row?.date || row?.shift_id === void 0 || row?.shift_id === null) {
61085
61533
  return null;
61086
61534
  }
@@ -61089,69 +61537,115 @@ function HomeView({
61089
61537
  date: row.date,
61090
61538
  shiftId: row.shift_id
61091
61539
  }];
61092
- }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61093
- const trendOptions = useMemo(() => {
61094
- if (!trendGroups || !userCompanyId) return null;
61540
+ }, [isBootstrapMonitorMode, legacyLineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61541
+ const legacyTrendOptions = useMemo(() => {
61542
+ if (isBootstrapMonitorMode || !legacyTrendGroups || !userCompanyId) return null;
61095
61543
  return {
61096
- groups: trendGroups,
61544
+ groups: legacyTrendGroups,
61097
61545
  companyId: userCompanyId,
61098
61546
  refreshKey: trendRefreshKey,
61099
61547
  forceRefresh: trendRefreshKey > 0
61100
61548
  };
61101
- }, [trendGroups, userCompanyId, trendRefreshKey]);
61102
- const { trend: kpiTrend } = useKpiTrends(trendOptions);
61103
- const hasFlowBuffers = Boolean(metricsMetadata?.hasFlowBuffers);
61549
+ }, [isBootstrapMonitorMode, legacyTrendGroups, userCompanyId, trendRefreshKey]);
61550
+ const { trend: legacyKpiTrend } = useKpiTrends(legacyTrendOptions);
61551
+ const bootstrapMonitor = useLiveMonitorBootstrap({
61552
+ lineIds: selectedLineIds,
61553
+ companyId: userCompanyId,
61554
+ enabled: shouldEnableMetricsFetch && !isLegacyMonitorMode
61555
+ });
61556
+ const currentWorkspaceMetrics = isBootstrapMonitorMode ? bootstrapMonitor.workspaceMetrics : legacyWorkspaceMetrics;
61557
+ const currentLineMetrics = isBootstrapMonitorMode ? bootstrapMonitor.lineMetrics : legacyLineMetrics;
61558
+ const currentEfficiencyLegend = isBootstrapMonitorMode ? bootstrapMonitor.efficiencyLegend : legacyEfficiencyLegend;
61559
+ const currentMetricsMetadata = isBootstrapMonitorMode ? bootstrapMonitor.metadata : legacyMetricsMetadata;
61560
+ const currentMetricsLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyMetricsLoading;
61561
+ const currentIsCurrentScopeResolved = isBootstrapMonitorMode ? bootstrapMonitor.isCurrentScopeResolved : legacyIsCurrentScopeResolved;
61562
+ const currentMetricsError = isBootstrapMonitorMode ? bootstrapMonitor.error : legacyMetricsError;
61563
+ const currentRefetchMetrics = isBootstrapMonitorMode ? bootstrapMonitor.refetch : refetchLegacyMetrics;
61564
+ const metricsDisplayNames = useMemo(() => {
61565
+ const nextDisplayNames = {};
61566
+ currentWorkspaceMetrics.forEach((workspace) => {
61567
+ if (!workspace.displayName) {
61568
+ return;
61569
+ }
61570
+ nextDisplayNames[`${workspace.line_id}_${workspace.workspace_name}`] = workspace.displayName;
61571
+ });
61572
+ return nextDisplayNames;
61573
+ }, [currentWorkspaceMetrics]);
61574
+ useEffect(() => {
61575
+ currentWorkspaceMetrics.forEach((workspace) => {
61576
+ if (!workspace.displayName) {
61577
+ return;
61578
+ }
61579
+ upsertWorkspaceDisplayNameInCache({
61580
+ lineId: workspace.line_id,
61581
+ workspaceId: workspace.workspace_name,
61582
+ displayName: workspace.displayName
61583
+ });
61584
+ });
61585
+ }, [currentWorkspaceMetrics]);
61586
+ const hasFlowBuffers = Boolean(currentMetricsMetadata?.hasFlowBuffers);
61104
61587
  useEffect(() => {
61105
- const sample = workspaceMetrics.slice(0, 3).map((workspace) => ({
61588
+ const sample = currentWorkspaceMetrics.slice(0, 3).map((workspace) => ({
61106
61589
  id: workspace.workspace_uuid || workspace.workspace_name,
61107
61590
  efficiency: workspace.efficiency,
61108
61591
  trend: workspace.trend,
61109
61592
  lineId: workspace.line_id
61110
61593
  }));
61111
61594
  logDebug3("[HomeView] workspaceMetrics update:", {
61112
- count: workspaceMetrics.length,
61113
- isLoading: metricsLoading,
61114
- error: metricsError?.message,
61595
+ count: currentWorkspaceMetrics.length,
61596
+ isLoading: currentMetricsLoading,
61597
+ error: currentMetricsError?.message,
61115
61598
  sample
61116
61599
  });
61117
- }, [workspaceMetrics, metricsLoading, metricsError]);
61118
- const kpis = useMemo(() => {
61119
- const lineMetricsRows = lineMetrics || [];
61600
+ }, [currentWorkspaceMetrics, currentMetricsLoading, currentMetricsError]);
61601
+ const buildDisplayKpis = useCallback((sourceLineMetrics, sourceIsLoading, sourceKpiTrend) => {
61602
+ const lineMetricsRows = sourceLineMetrics || [];
61603
+ let baseKpis = null;
61120
61604
  if (selectedLineIds.length > 1) {
61121
- const rowsForSelectedLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
61122
- if (metricsLoading && rowsForSelectedLines.length === 0) return null;
61123
- return aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
61124
- }
61125
- const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
61126
- if (!row) {
61127
- if (metricsLoading) return null;
61128
- return buildKPIsFromLineMetricsRow(null);
61129
- }
61130
- return buildKPIsFromLineMetricsRow(row);
61131
- }, [lineMetrics, metricsLoading, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61132
- const kpisWithTrend = useMemo(() => {
61133
- if (!kpis) return null;
61134
- if (!kpiTrend) return kpis;
61605
+ const rowsForSelectedLines = lineMetricsRows.filter((row) => selectedLineIdSet.has(row?.line_id));
61606
+ if (sourceIsLoading && rowsForSelectedLines.length === 0) return null;
61607
+ baseKpis = aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
61608
+ } else {
61609
+ const row = lineMetricsRows.find((candidate) => candidate?.line_id === primarySelectedLineId);
61610
+ if (!row) {
61611
+ if (sourceIsLoading) return null;
61612
+ baseKpis = buildKPIsFromLineMetricsRow(null);
61613
+ } else {
61614
+ baseKpis = buildKPIsFromLineMetricsRow(row);
61615
+ }
61616
+ }
61617
+ if (!baseKpis || !sourceKpiTrend) {
61618
+ return baseKpis;
61619
+ }
61135
61620
  return {
61136
- ...kpis,
61621
+ ...baseKpis,
61137
61622
  efficiency: {
61138
- ...kpis.efficiency,
61139
- change: kpiTrend.efficiency?.delta_pp ?? kpis.efficiency.change
61623
+ ...baseKpis.efficiency,
61624
+ change: sourceKpiTrend.efficiency?.delta_pp ?? baseKpis.efficiency.change
61140
61625
  },
61141
61626
  outputProgress: {
61142
- ...kpis.outputProgress,
61143
- change: kpiTrend.outputProgress?.delta_pp ?? kpis.outputProgress.change
61627
+ ...baseKpis.outputProgress,
61628
+ change: sourceKpiTrend.outputProgress?.delta_pp ?? baseKpis.outputProgress.change
61144
61629
  },
61145
61630
  underperformingWorkers: {
61146
- ...kpis.underperformingWorkers,
61147
- change: kpiTrend.underperformingWorkers?.delta_count ?? kpis.underperformingWorkers.change
61631
+ ...baseKpis.underperformingWorkers,
61632
+ change: sourceKpiTrend.underperformingWorkers?.delta_count ?? baseKpis.underperformingWorkers.change
61148
61633
  },
61149
61634
  avgCycleTime: {
61150
- ...kpis.avgCycleTime,
61151
- change: kpiTrend.avgCycleTime?.delta_seconds ?? kpis.avgCycleTime.change
61635
+ ...baseKpis.avgCycleTime,
61636
+ change: sourceKpiTrend.avgCycleTime?.delta_seconds ?? baseKpis.avgCycleTime.change
61152
61637
  }
61153
61638
  };
61154
- }, [kpis, kpiTrend]);
61639
+ }, [primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61640
+ const legacyKpisWithTrend = useMemo(
61641
+ () => buildDisplayKpis(legacyLineMetrics, legacyMetricsLoading, legacyKpiTrend),
61642
+ [buildDisplayKpis, legacyLineMetrics, legacyMetricsLoading, legacyKpiTrend]
61643
+ );
61644
+ const bootstrapKpisWithTrend = useMemo(
61645
+ () => buildDisplayKpis(bootstrapMonitor.lineMetrics, bootstrapMonitor.isLoading, bootstrapMonitor.kpiTrend),
61646
+ [buildDisplayKpis, bootstrapMonitor.lineMetrics, bootstrapMonitor.isLoading, bootstrapMonitor.kpiTrend]
61647
+ );
61648
+ const currentKpisWithTrend = isBootstrapMonitorMode ? bootstrapKpisWithTrend : legacyKpisWithTrend;
61155
61649
  const selectedLineMeta = useMemo(
61156
61650
  () => selectedLineIds.length === 1 ? dbLines.find((line) => line.id === primarySelectedLineId) : void 0,
61157
61651
  [dbLines, primarySelectedLineId, selectedLineIds.length]
@@ -61160,40 +61654,121 @@ function HomeView({
61160
61654
  const isUptimeMode = selectedMonitoringMode === "uptime";
61161
61655
  const averageIdleTimeSeconds = useMemo(() => {
61162
61656
  if (!isUptimeMode) return null;
61163
- const targetWorkspaces = workspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
61657
+ const targetWorkspaces = currentWorkspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
61164
61658
  const idleValues = targetWorkspaces.map((ws) => ws.idle_time).filter((value) => Number.isFinite(value));
61165
61659
  if (idleValues.length === 0) return 0;
61166
61660
  const totalIdle = idleValues.reduce((sum, value) => sum + value, 0);
61167
61661
  return totalIdle / idleValues.length;
61168
- }, [isUptimeMode, primarySelectedLineId, workspaceMetrics]);
61662
+ }, [isUptimeMode, primarySelectedLineId, currentWorkspaceMetrics]);
61169
61663
  const {
61170
- activeBreaks: allActiveBreaks,
61171
- isLoading: breaksLoading,
61172
- error: breaksError
61173
- } = useActiveBreaks(visibleLineIds);
61174
- const activeBreaks = useMemo(() => {
61664
+ activeBreaks: legacyAllActiveBreaks,
61665
+ isLoading: legacyBreaksLoading,
61666
+ error: legacyBreaksError
61667
+ } = useActiveBreaks(isBootstrapMonitorMode ? EMPTY_LINE_IDS : visibleLineIds);
61668
+ const legacyActiveBreaks = useMemo(() => {
61175
61669
  if (isAllLinesSelection(selectedLineIds)) {
61176
- return allActiveBreaks;
61670
+ return legacyAllActiveBreaks;
61177
61671
  }
61178
- return allActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
61179
- }, [allActiveBreaks, selectedLineIdSet, selectedLineIds]);
61672
+ return legacyAllActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
61673
+ }, [legacyAllActiveBreaks, selectedLineIdSet, selectedLineIds]);
61674
+ const currentActiveBreaks = isBootstrapMonitorMode ? bootstrapMonitor.activeBreaks : legacyActiveBreaks;
61675
+ const currentBreaksLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyBreaksLoading;
61676
+ const currentBreaksError = isBootstrapMonitorMode ? bootstrapMonitor.error?.message || null : legacyBreaksError;
61180
61677
  const activeBreakLineIds = useMemo(
61181
- () => new Set(activeBreaks.map((breakItem) => breakItem.lineId)),
61182
- [activeBreaks]
61678
+ () => new Set(currentActiveBreaks.map((breakItem) => breakItem.lineId)),
61679
+ [currentActiveBreaks]
61183
61680
  );
61184
61681
  const workspaceMetricsWithBreakState = useMemo(
61185
- () => workspaceMetrics.map((workspace) => ({
61682
+ () => currentWorkspaceMetrics.map((workspace) => ({
61186
61683
  ...workspace,
61187
61684
  scheduled_break_active: activeBreakLineIds.has(workspace.line_id)
61188
61685
  })),
61189
- [workspaceMetrics, activeBreakLineIds]
61686
+ [currentWorkspaceMetrics, activeBreakLineIds]
61190
61687
  );
61191
61688
  const [breakNotificationsDismissed, setBreakNotificationsDismissed] = useState(false);
61192
61689
  useEffect(() => {
61193
- if (activeBreaks.length > 0) {
61690
+ if (currentActiveBreaks.length > 0) {
61194
61691
  setBreakNotificationsDismissed(false);
61195
61692
  }
61196
- }, [activeBreaks.length]);
61693
+ }, [currentActiveBreaks.length]);
61694
+ const {
61695
+ streamsByWorkspaceId: legacyVideoStreamsByWorkspaceId,
61696
+ isLoading: legacyVideoStreamsLoading
61697
+ } = useWorkspaceVideoStreams(isBootstrapMonitorMode ? EMPTY_WORKSPACES : legacyWorkspaceMetrics, {
61698
+ lineIds: isBootstrapMonitorMode ? EMPTY_LINE_IDS : selectedLineIds
61699
+ });
61700
+ const currentVideoStreamsByWorkspaceId = isBootstrapMonitorMode ? bootstrapMonitor.videoStreamsByWorkspaceId : legacyVideoStreamsByWorkspaceId;
61701
+ const currentVideoStreamsLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyVideoStreamsLoading;
61702
+ useEffect(() => {
61703
+ if (!isShadowMonitorMode) {
61704
+ lastShadowMismatchSignatureRef.current = null;
61705
+ return;
61706
+ }
61707
+ if (!shouldEnableMetricsFetch || legacyMetricsLoading || !legacyIsCurrentScopeResolved || bootstrapMonitor.isLoading || !bootstrapMonitor.isCurrentScopeResolved) {
61708
+ return;
61709
+ }
61710
+ const legacySnapshot = normalizeMonitorShadowSnapshot({
61711
+ selectedLineIds,
61712
+ workspaces: workspaceMetricsWithBreakState,
61713
+ kpis: legacyKpisWithTrend,
61714
+ kpiTrend: legacyKpiTrend,
61715
+ activeBreaks: legacyActiveBreaks,
61716
+ videoStreamsByWorkspaceId: legacyVideoStreamsByWorkspaceId,
61717
+ efficiencyLegend: legacyEfficiencyLegend
61718
+ });
61719
+ const bootstrapSnapshot = normalizeMonitorShadowSnapshot({
61720
+ selectedLineIds,
61721
+ resolvedScope: bootstrapMonitor.resolvedScope,
61722
+ lines: bootstrapMonitor.lines,
61723
+ workspaces: bootstrapMonitor.workspaceMetrics.map((workspace) => ({
61724
+ ...workspace,
61725
+ scheduled_break_active: bootstrapMonitor.activeBreaks.some((activeBreak) => activeBreak.lineId === workspace.line_id)
61726
+ })),
61727
+ kpis: bootstrapKpisWithTrend,
61728
+ kpiTrend: bootstrapMonitor.kpiTrend,
61729
+ activeBreaks: bootstrapMonitor.activeBreaks,
61730
+ videoStreamsByWorkspaceId: bootstrapMonitor.videoStreamsByWorkspaceId,
61731
+ efficiencyLegend: bootstrapMonitor.efficiencyLegend
61732
+ });
61733
+ const mismatches = diffMonitorShadowSnapshots(legacySnapshot, bootstrapSnapshot);
61734
+ const signature = JSON.stringify(mismatches);
61735
+ if (mismatches.length === 0) {
61736
+ lastShadowMismatchSignatureRef.current = null;
61737
+ return;
61738
+ }
61739
+ if (lastShadowMismatchSignatureRef.current === signature) {
61740
+ return;
61741
+ }
61742
+ lastShadowMismatchSignatureRef.current = signature;
61743
+ console.error("[HomeView][monitor-shadow-mismatch]", {
61744
+ selectedLineIds,
61745
+ mismatches,
61746
+ legacySnapshot,
61747
+ bootstrapSnapshot
61748
+ });
61749
+ }, [
61750
+ bootstrapKpisWithTrend,
61751
+ bootstrapMonitor.activeBreaks,
61752
+ bootstrapMonitor.efficiencyLegend,
61753
+ bootstrapMonitor.isCurrentScopeResolved,
61754
+ bootstrapMonitor.isLoading,
61755
+ bootstrapMonitor.kpiTrend,
61756
+ bootstrapMonitor.lines,
61757
+ bootstrapMonitor.resolvedScope,
61758
+ bootstrapMonitor.videoStreamsByWorkspaceId,
61759
+ bootstrapMonitor.workspaceMetrics,
61760
+ isShadowMonitorMode,
61761
+ legacyActiveBreaks,
61762
+ legacyEfficiencyLegend,
61763
+ legacyIsCurrentScopeResolved,
61764
+ legacyKpiTrend,
61765
+ legacyKpisWithTrend,
61766
+ legacyMetricsLoading,
61767
+ legacyVideoStreamsByWorkspaceId,
61768
+ selectedLineIds,
61769
+ shouldEnableMetricsFetch,
61770
+ workspaceMetricsWithBreakState
61771
+ ]);
61197
61772
  const showBottleneckNotification = useCallback(async (bottleneck) => {
61198
61773
  try {
61199
61774
  logDebug3("\u{1F514} [Notification] Raw bottleneck data:", bottleneck);
@@ -61371,6 +61946,9 @@ function HomeView({
61371
61946
  setBottleneckNotification(errorNotification);
61372
61947
  }
61373
61948
  }, [notificationService, timezone, dashboardConfig, lineShiftConfigs]);
61949
+ useEffect(() => {
61950
+ showBottleneckNotificationRef.current = showBottleneckNotification;
61951
+ }, [showBottleneckNotification]);
61374
61952
  useEffect(() => {
61375
61953
  const ticketsEnabled = dashboardConfig?.ticketsConfig?.enabled ?? true;
61376
61954
  if (!ticketsEnabled) {
@@ -61440,7 +62018,7 @@ function HomeView({
61440
62018
  });
61441
62019
  if (latestTicket.event_type === "bottleneck") {
61442
62020
  logDebug3("\u{1F514} [Polling] Showing bottleneck notification for:", latestTicket.log_number);
61443
- showBottleneckNotification(latestTicket);
62021
+ await showBottleneckNotificationRef.current?.(latestTicket);
61444
62022
  } else {
61445
62023
  logDebug3("\u26A0\uFE0F [Polling] Non-bottleneck ticket found but no handler configured:", latestTicket.event_type);
61446
62024
  }
@@ -61460,29 +62038,30 @@ function HomeView({
61460
62038
  clearInterval(pollInterval);
61461
62039
  clearTimeout(initialPollTimer);
61462
62040
  };
61463
- }, [notificationService, userCompanyId, user, showBottleneckNotification, dashboardConfig?.ticketsConfig?.enabled]);
62041
+ }, [
62042
+ dashboardConfig?.ticketsConfig?.enabled,
62043
+ notificationService,
62044
+ user?.email,
62045
+ user?.role_level,
62046
+ userCompanyId
62047
+ ]);
61464
62048
  const handleWorkspaceHover = useCallback((workspaceId) => {
61465
62049
  }, []);
61466
62050
  const handleWorkspaceHoverEnd = useCallback((workspaceId) => {
61467
62051
  }, []);
61468
- const {
61469
- streamsByWorkspaceId: videoStreamsByWorkspaceId,
61470
- isLoading: videoStreamsLoading
61471
- } = useWorkspaceVideoStreams(workspaceMetrics);
61472
- const memoizedKPIs = useMemo(() => kpisWithTrend, [
62052
+ const memoizedKPIs = useMemo(() => currentKpisWithTrend, [
61473
62053
  // Only update reference when values change by at least 1%
61474
- kpisWithTrend?.efficiency?.value ? Math.round(kpisWithTrend.efficiency.value) : null,
61475
- kpisWithTrend?.efficiency?.change,
61476
- kpisWithTrend?.underperformingWorkers?.current,
61477
- kpisWithTrend?.underperformingWorkers?.total,
61478
- kpisWithTrend?.underperformingWorkers?.change,
61479
- kpisWithTrend?.outputProgress?.current,
61480
- kpisWithTrend?.outputProgress?.target,
61481
- kpisWithTrend?.outputProgress?.change,
61482
- kpisWithTrend?.avgCycleTime?.value ? Math.round(kpisWithTrend.avgCycleTime.value * 10) / 10 : null,
61483
- // Round to 1 decimal
61484
- kpisWithTrend?.avgCycleTime?.change,
61485
- kpisWithTrend?.qualityCompliance?.value ? Math.round(kpisWithTrend.qualityCompliance.value) : null,
62054
+ currentKpisWithTrend?.efficiency?.value ? Math.round(currentKpisWithTrend.efficiency.value) : null,
62055
+ currentKpisWithTrend?.efficiency?.change,
62056
+ currentKpisWithTrend?.underperformingWorkers?.current,
62057
+ currentKpisWithTrend?.underperformingWorkers?.total,
62058
+ currentKpisWithTrend?.underperformingWorkers?.change,
62059
+ currentKpisWithTrend?.outputProgress?.current,
62060
+ currentKpisWithTrend?.outputProgress?.target,
62061
+ currentKpisWithTrend?.outputProgress?.change,
62062
+ currentKpisWithTrend?.avgCycleTime?.value ? Math.round(currentKpisWithTrend.avgCycleTime.value * 10) / 10 : null,
62063
+ currentKpisWithTrend?.avgCycleTime?.change,
62064
+ currentKpisWithTrend?.qualityCompliance?.value ? Math.round(currentKpisWithTrend.qualityCompliance.value) : null,
61486
62065
  selectedLineIdsKey
61487
62066
  ]);
61488
62067
  useEffect(() => {
@@ -61493,13 +62072,13 @@ function HomeView({
61493
62072
  dashboard_surface: "monitor"
61494
62073
  });
61495
62074
  }, []);
61496
- const metricsErrorMessage = metricsError?.message || null;
62075
+ const metricsErrorMessage = currentMetricsError?.message || null;
61497
62076
  const handleRetryDashboardData = useCallback(async () => {
61498
62077
  if (isRecoveringSession) {
61499
62078
  await retrySessionHydration();
61500
62079
  }
61501
- refetchMetrics();
61502
- }, [isRecoveringSession, refetchMetrics, retrySessionHydration]);
62080
+ await currentRefetchMetrics();
62081
+ }, [currentRefetchMetrics, isRecoveringSession, retrySessionHydration]);
61503
62082
  const getTrackedLineScope = useCallback((lineIdsForScope) => {
61504
62083
  if (isAllLinesSelection(lineIdsForScope)) {
61505
62084
  return factoryViewId;
@@ -61552,12 +62131,15 @@ function HomeView({
61552
62131
  updateSelectedLineIds(Array.from(currentSelection));
61553
62132
  }, [selectedLineIds, updateSelectedLineIds]);
61554
62133
  useEffect(() => {
61555
- if (!metricsLoading && isChangingFilter) {
62134
+ if (!isChangingFilter) {
62135
+ return;
62136
+ }
62137
+ if (!currentMetricsLoading && currentIsCurrentScopeResolved || currentMetricsError) {
61556
62138
  setIsChangingFilter(false);
61557
62139
  }
61558
- }, [metricsLoading, isChangingFilter]);
62140
+ }, [currentIsCurrentScopeResolved, currentMetricsError, currentMetricsLoading, isChangingFilter]);
61559
62141
  useEffect(() => {
61560
- if (shouldEnableMetricsFetch && !metricsLoading && !metricsError && !hasInitialDataLoaded) {
62142
+ if (shouldEnableMetricsFetch && !currentMetricsLoading && !currentMetricsError && !hasInitialDataLoaded) {
61561
62143
  setHasInitialDataLoaded(true);
61562
62144
  trackCoreEvent("monitor page loaded", {
61563
62145
  default_line_id: defaultLineId,
@@ -61566,7 +62148,7 @@ function HomeView({
61566
62148
  dashboard_surface: "monitor"
61567
62149
  });
61568
62150
  }
61569
- }, [shouldEnableMetricsFetch, metricsLoading, metricsError, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
62151
+ }, [shouldEnableMetricsFetch, currentMetricsLoading, currentMetricsError, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
61570
62152
  const lineTitle = useMemo(() => {
61571
62153
  return factoryName;
61572
62154
  }, [factoryName]);
@@ -61717,15 +62299,15 @@ function HomeView({
61717
62299
  return showLoading;
61718
62300
  };
61719
62301
  const isAuthBootstrapping = authStatus === "loading";
61720
- const isInitialLoading = !isHydrated || !hasInitialDataLoaded && (isAuthBootstrapping || shouldEnableMetricsFetch && metricsLoading);
61721
- const isDataLoading = metricsLoading;
62302
+ const isInitialLoading = !isHydrated || !hasInitialDataLoaded && (isAuthBootstrapping || shouldEnableMetricsFetch && currentMetricsLoading);
62303
+ const isDataLoading = currentMetricsLoading;
61722
62304
  const hasKpiDataReady = useMemo(() => {
61723
- const lineMetricsRows = lineMetrics || [];
62305
+ const lineMetricsRows = currentLineMetrics || [];
61724
62306
  if (selectedLineIds.length > 1) {
61725
62307
  return lineMetricsRows.some((row) => selectedLineIdSet.has(row?.line_id));
61726
62308
  }
61727
62309
  return lineMetricsRows.some((row) => row?.line_id === primarySelectedLineId);
61728
- }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
62310
+ }, [currentLineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61729
62311
  const isKpiLoading = !hasKpiDataReady;
61730
62312
  const shouldShowReconnectScreen = !hasInitialDataLoaded && (isRecoveringSession || !shouldEnableMetricsFetch && authStatus !== "failed" || !!metricsErrorMessage && authStatus !== "failed");
61731
62313
  const shouldShowFatalLoadFailure = !hasInitialDataLoaded && !!metricsErrorMessage && authStatus === "failed";
@@ -61753,6 +62335,7 @@ function HomeView({
61753
62335
  return () => clearTimeout(timer);
61754
62336
  }, [isDataLoading]);
61755
62337
  const shouldShowDataLoading = showDataLoading || isDataLoading;
62338
+ const shouldShowFilterChangeLoader = isChangingFilter && !currentIsCurrentScopeResolved;
61756
62339
  const shouldShowKpiLoading = useSmoothLoading(isKpiLoading, 400);
61757
62340
  const displayKpis = shouldShowKpiLoading ? null : memoizedKPIs;
61758
62341
  const kpiSectionControl = useMemo(() => /* @__PURE__ */ jsx(
@@ -61837,7 +62420,7 @@ function HomeView({
61837
62420
  }
61838
62421
  )
61839
62422
  ] }) }) : null,
61840
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsx(
62423
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsx("div", { className: "relative flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsx(
61841
62424
  motion.div,
61842
62425
  {
61843
62426
  initial: { opacity: 0, scale: 0.98 },
@@ -61849,10 +62432,10 @@ function HomeView({
61849
62432
  lineNames: mergedLineNames,
61850
62433
  lineOrder: selectedLineIds,
61851
62434
  factoryView: factoryViewId,
61852
- legend: efficiencyLegend,
62435
+ legend: currentEfficiencyLegend,
61853
62436
  videoSources,
61854
- videoStreamsByWorkspaceId,
61855
- videoStreamsLoading,
62437
+ videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
62438
+ videoStreamsLoading: currentVideoStreamsLoading,
61856
62439
  displayNames: metricsDisplayNames,
61857
62440
  hasFlowBuffers,
61858
62441
  className: "h-full",
@@ -61860,8 +62443,7 @@ function HomeView({
61860
62443
  onWorkspaceHover: handleWorkspaceHover,
61861
62444
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
61862
62445
  })
61863
- },
61864
- selectedLineIdsKey
62446
+ }
61865
62447
  ) : !shouldShowDataLoading && hasInitialDataLoaded ? /* @__PURE__ */ jsx(
61866
62448
  motion.div,
61867
62449
  {
@@ -61883,10 +62465,10 @@ function HomeView({
61883
62465
  lineNames: mergedLineNames,
61884
62466
  lineOrder: selectedLineIds,
61885
62467
  factoryView: factoryViewId,
61886
- legend: efficiencyLegend,
62468
+ legend: currentEfficiencyLegend,
61887
62469
  videoSources,
61888
- videoStreamsByWorkspaceId,
61889
- videoStreamsLoading,
62470
+ videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
62471
+ videoStreamsLoading: currentVideoStreamsLoading,
61890
62472
  displayNames: metricsDisplayNames,
61891
62473
  hasFlowBuffers,
61892
62474
  className: "h-full",
@@ -61894,16 +62476,15 @@ function HomeView({
61894
62476
  onWorkspaceHover: handleWorkspaceHover,
61895
62477
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
61896
62478
  })
61897
- },
61898
- selectedLineIdsKey
62479
+ }
61899
62480
  ) }) })
61900
62481
  ] }),
61901
62482
  /* @__PURE__ */ jsx(
61902
62483
  BreakNotificationPopup,
61903
62484
  {
61904
- activeBreaks,
62485
+ activeBreaks: currentActiveBreaks,
61905
62486
  lineNames: mergedLineNames,
61906
- isVisible: !breaksLoading && !breaksError && !breakNotificationsDismissed,
62487
+ isVisible: !currentBreaksLoading && !currentBreaksError && !breakNotificationsDismissed,
61907
62488
  onDismiss: () => setBreakNotificationsDismissed(true)
61908
62489
  }
61909
62490
  ),
@@ -61915,6 +62496,15 @@ function HomeView({
61915
62496
  onDismiss: () => setBottleneckNotification(null)
61916
62497
  }
61917
62498
  ),
62499
+ /* @__PURE__ */ jsx(
62500
+ LoadingOverlayCmp,
62501
+ {
62502
+ isVisible: shouldShowFilterChangeLoader,
62503
+ message: "Loading Dashboard...",
62504
+ className: "bg-slate-50/30 backdrop-blur-[3px]",
62505
+ contentVariant: "plain"
62506
+ }
62507
+ ),
61918
62508
  diagnosisModalOpen && bottleneckModalData?.clipId && /* @__PURE__ */ jsx(
61919
62509
  DiagnosisVideoModal,
61920
62510
  {
@@ -62165,7 +62755,7 @@ var buildLineInfoSnapshot = (lineDetails, metrics2) => {
62165
62755
  }
62166
62756
  };
62167
62757
  };
62168
- var transformWorkspaceMetrics = (workspaceData, lineId, companyId, queryDate, queryShiftId) => (workspaceData || []).map((item) => ({
62758
+ var transformWorkspaceMetrics2 = (workspaceData, lineId, companyId, queryDate, queryShiftId) => (workspaceData || []).map((item) => ({
62169
62759
  company_id: item.company_id || companyId,
62170
62760
  line_id: item.line_id || lineId,
62171
62761
  shift_id: item.shift_id ?? queryShiftId,
@@ -62313,7 +62903,7 @@ var useLineDetailPageData = ({
62313
62903
  setDetailResponse(nextDetail);
62314
62904
  const metrics3 = transformLineMetrics(lineId, nextDetail, queryDate, queryShiftId);
62315
62905
  const lineDetails2 = transformLineDetails(lineId, nextDetail);
62316
- const workspaces2 = transformWorkspaceMetrics(
62906
+ const workspaces2 = transformWorkspaceMetrics2(
62317
62907
  nextDetail.workspace_metrics || [],
62318
62908
  lineId,
62319
62909
  companyId,
@@ -62403,7 +62993,7 @@ var useLineDetailPageData = ({
62403
62993
  [detailResponse, lineId]
62404
62994
  );
62405
62995
  const workspaces = useMemo(
62406
- () => detailResponse && companyId ? transformWorkspaceMetrics(detailResponse.workspace_metrics || [], lineId, companyId, queryDate, queryShiftId) : [],
62996
+ () => detailResponse && companyId ? transformWorkspaceMetrics2(detailResponse.workspace_metrics || [], lineId, companyId, queryDate, queryShiftId) : [],
62407
62997
  [detailResponse, companyId, lineId, queryDate, queryShiftId]
62408
62998
  );
62409
62999
  const lineInfo = useMemo(() => buildLineInfoSnapshot(lineDetails, metrics2), [lineDetails, metrics2]);
@@ -74025,7 +74615,8 @@ var TeamManagementView = ({
74025
74615
  const bootstrapData = await bootstrapResponse.json();
74026
74616
  setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
74027
74617
  id: factory.id,
74028
- factory_name: factory.factory_name
74618
+ factory_name: factory.factory_name,
74619
+ company_id: factory.company_id
74029
74620
  })));
74030
74621
  setAvailableLines(bootstrapData.lines || []);
74031
74622
  setUsers(bootstrapData.users || []);
@@ -78993,7 +79584,7 @@ var efficiencyLineConfig = [
78993
79584
  var bumpRenderCounter = (key) => {
78994
79585
  if (process.env.NODE_ENV === "test") ;
78995
79586
  };
78996
- var toNumber3 = (value) => {
79587
+ var toNumber4 = (value) => {
78997
79588
  if (typeof value === "number" && Number.isFinite(value)) return value;
78998
79589
  if (typeof value === "string" && value.trim().length > 0) {
78999
79590
  const parsed = Number(value);
@@ -79528,7 +80119,7 @@ var OverviewSummaryCards = React143__default.memo(({ store }) => {
79528
80119
  workspaceId: item.workspace_id || "",
79529
80120
  workspaceName: item.workspace_name?.trim() || item.workspace_id || "Unknown",
79530
80121
  lineName: item.line_name?.trim() || "Unknown Line",
79531
- avgIdleSeconds: toNumber3(item.avg_idle_seconds)
80122
+ avgIdleSeconds: toNumber4(item.avg_idle_seconds)
79532
80123
  })).slice(0, 5);
79533
80124
  }, [snapshot.data.summary.avg_idle_per_workstation?.top_contributors]);
79534
80125
  const showIdleContributorLineNames = React143__default.useMemo(() => {
@@ -79743,9 +80334,9 @@ var PoorestPerformersCard = React143__default.memo(({
79743
80334
  return {
79744
80335
  id: lineId,
79745
80336
  name: line.line_name?.trim() || "Unknown Line",
79746
- efficiency: roundOne(toNumber3(line.avg_efficiency) || 0),
79747
- previousEfficiency: toNumber3(line.previous_avg_efficiency),
79748
- delta: toNumber3(line.delta_pp),
80337
+ efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
80338
+ previousEfficiency: toNumber4(line.previous_avg_efficiency),
80339
+ delta: toNumber4(line.delta_pp),
79749
80340
  supervisor: supervisor?.displayName || "Unassigned",
79750
80341
  supervisorImage: supervisor?.profilePhotoUrl ?? null
79751
80342
  };
@@ -79854,14 +80445,14 @@ var IdleBreakdownCard = React143__default.memo(({
79854
80445
  paletteToken: item.palette_token?.trim(),
79855
80446
  iconToken: item.icon_token?.trim(),
79856
80447
  isKnown: item.is_known ?? void 0,
79857
- value: toNumber3(item.percentage) || 0,
79858
- totalDurationSeconds: toNumber3(item.total_duration_seconds),
79859
- efficiencyLossPercentage: toNumber3(item.efficiency_loss_percentage),
80448
+ value: toNumber4(item.percentage) || 0,
80449
+ totalDurationSeconds: toNumber4(item.total_duration_seconds),
80450
+ efficiencyLossPercentage: toNumber4(item.efficiency_loss_percentage),
79860
80451
  contributors: (item.contributors || []).map((contributor) => ({
79861
80452
  workspaceId: contributor.workspace_id || "",
79862
80453
  workspaceName: contributor.workspace_name?.trim() || "Unknown",
79863
- totalDurationSeconds: toNumber3(contributor.total_duration_seconds),
79864
- percentageWithinReason: toNumber3(contributor.percentage_within_reason)
80454
+ totalDurationSeconds: toNumber4(contributor.total_duration_seconds),
80455
+ percentageWithinReason: toNumber4(contributor.percentage_within_reason)
79865
80456
  }))
79866
80457
  })).filter((item) => item.value > 0);
79867
80458
  }, [idle.data]);
@@ -79930,7 +80521,7 @@ var EfficiencyTrendCard = React143__default.memo(({
79930
80521
  return `${hour12}:${minutes.toString().padStart(2, "0")} ${suffix}`;
79931
80522
  })(),
79932
80523
  efficiency: (() => {
79933
- const value = toNumber3(point.avg_efficiency);
80524
+ const value = toNumber4(point.avg_efficiency);
79934
80525
  return value === null ? void 0 : value;
79935
80526
  })()
79936
80527
  }));
@@ -79943,7 +80534,7 @@ var EfficiencyTrendCard = React143__default.memo(({
79943
80534
  name: format(pointDate, "MMM d"),
79944
80535
  dayOfWeek: format(pointDate, "EEEE"),
79945
80536
  efficiency: (() => {
79946
- const value = toNumber3(point.avg_efficiency);
80537
+ const value = toNumber4(point.avg_efficiency);
79947
80538
  return value === null ? void 0 : value;
79948
80539
  })()
79949
80540
  }]];
@@ -79967,7 +80558,7 @@ var EfficiencyTrendCard = React143__default.memo(({
79967
80558
  name: pointDate ? format(pointDate, "MMM d") : "",
79968
80559
  dayOfWeek: pointDate ? format(pointDate, "EEEE") : "",
79969
80560
  efficiency: (() => {
79970
- const value = toNumber3(point.avg_efficiency);
80561
+ const value = toNumber4(point.avg_efficiency);
79971
80562
  return value === null ? void 0 : value;
79972
80563
  })()
79973
80564
  };
@@ -80091,7 +80682,7 @@ var debugRefreshLog = (message, payload) => {
80091
80682
  var isAbortError2 = (error) => {
80092
80683
  return error instanceof DOMException && error.name === "AbortError";
80093
80684
  };
80094
- var toNumber4 = (value) => {
80685
+ var toNumber5 = (value) => {
80095
80686
  if (typeof value === "number" && Number.isFinite(value)) return value;
80096
80687
  if (typeof value === "string" && value.trim().length > 0) {
80097
80688
  const parsed = Number(value);
@@ -80100,11 +80691,11 @@ var toNumber4 = (value) => {
80100
80691
  return null;
80101
80692
  };
80102
80693
  var formatImprovementMetric = (recommendation) => {
80103
- const estimatedGain = toNumber4(recommendation.estimated_gain_pieces);
80694
+ const estimatedGain = toNumber5(recommendation.estimated_gain_pieces);
80104
80695
  if (estimatedGain !== null && estimatedGain > 0) {
80105
80696
  return `+${Math.round(estimatedGain).toLocaleString()} pcs / day`;
80106
80697
  }
80107
- const idleMinutes = toNumber4(recommendation.metrics?.idle_minutes);
80698
+ const idleMinutes = toNumber5(recommendation.metrics?.idle_minutes);
80108
80699
  if (idleMinutes !== null) {
80109
80700
  return `-${Math.round(idleMinutes)}m Idle`;
80110
80701
  }