@optifye/dashboard-core 6.11.43 → 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.js CHANGED
@@ -13487,10 +13487,19 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
13487
13487
 
13488
13488
  // src/lib/hooks/useDashboardMetrics.ts
13489
13489
  var DEBUG_DASHBOARD_LOGS = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
13490
+ var REALTIME_REFRESH_DEBOUNCE_MS = 1500;
13491
+ var REALTIME_REFRESH_MIN_INTERVAL_MS = 5e3;
13490
13492
  var logDebug = (...args) => {
13491
13493
  if (!DEBUG_DASHBOARD_LOGS) return;
13492
13494
  console.log(...args);
13493
13495
  };
13496
+ var buildMetricsScopeKey = (lineId, lineIds) => {
13497
+ const normalizedLineIds = Array.from(new Set((lineIds || []).filter(Boolean))).sort();
13498
+ if (normalizedLineIds.length > 0) {
13499
+ return `${lineId}|${normalizedLineIds.join(",")}`;
13500
+ }
13501
+ return lineId;
13502
+ };
13494
13503
  var parseEfficiencyLegend = (legend) => {
13495
13504
  if (!legend) return null;
13496
13505
  const coerce = (value, fallback) => {
@@ -13570,6 +13579,7 @@ var useDashboardMetrics = ({
13570
13579
  const supabase = useSupabase();
13571
13580
  const [metrics2, setMetrics] = React143.useState({ workspaceMetrics: [], lineMetrics: [] });
13572
13581
  const [metricsLineId, setMetricsLineId] = React143.useState(lineId ?? null);
13582
+ const [metricsScopeKey, setMetricsScopeKey] = React143.useState(() => buildMetricsScopeKey(lineId, lineIds));
13573
13583
  const [isLoading, setIsLoading] = React143.useState(true);
13574
13584
  const [error, setError] = React143.useState(null);
13575
13585
  const lineIdRef = React143.useRef(lineId);
@@ -13579,8 +13589,10 @@ var useDashboardMetrics = ({
13579
13589
  const abortControllerRef = React143.useRef(null);
13580
13590
  const lastFetchKeyRef = React143.useRef(null);
13581
13591
  const inFlightFetchKeyRef = React143.useRef(null);
13582
- const updateQueueRef = React143.useRef(false);
13583
13592
  const onLineMetricsUpdateRef = React143.useRef(onLineMetricsUpdate);
13593
+ const pendingRealtimeRefreshRef = React143.useRef(false);
13594
+ const realtimeRefreshTimerRef = React143.useRef(null);
13595
+ const lastRealtimeRefreshStartedAtRef = React143.useRef(0);
13584
13596
  const shiftGroupsRef = React143.useRef(shiftGroups);
13585
13597
  const operationalShiftKeyRef = React143.useRef(operationalShiftKey);
13586
13598
  const configuredLineIdsRef = React143.useRef(configuredLineIds);
@@ -13608,15 +13620,17 @@ var useDashboardMetrics = ({
13608
13620
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
13609
13621
  [entityConfig.companyId]
13610
13622
  );
13623
+ const requestedScopeKey = React143.useMemo(
13624
+ () => buildMetricsScopeKey(lineId, isFactoryView ? targetFactoryLineIds : void 0),
13625
+ [isFactoryView, lineId, targetFactoryLineIds]
13626
+ );
13611
13627
  React143.useEffect(() => {
13612
13628
  lineIdRef.current = lineId;
13613
- setMetrics({ workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND });
13614
- setMetricsLineId(null);
13615
13629
  setIsLoading(true);
13616
13630
  setError(null);
13617
13631
  lastFetchKeyRef.current = null;
13618
13632
  inFlightFetchKeyRef.current = null;
13619
- }, [lineId]);
13633
+ }, [lineId, requestedScopeKey]);
13620
13634
  const fetchAllMetrics = React143.useCallback(async (options = {}) => {
13621
13635
  const { force = false } = options;
13622
13636
  const currentLineIdToUse = lineIdRef.current;
@@ -13629,7 +13643,6 @@ var useDashboardMetrics = ({
13629
13643
  companySpecificMetricsTable
13630
13644
  });
13631
13645
  if (!currentLineIdToUse || !supabase || !enabled || shiftLoading || isTimezoneLoading || companySpecificMetricsTable.includes("unknown_company")) {
13632
- updateQueueRef.current = false;
13633
13646
  if (!metrics2?.workspaceMetrics?.length && !metrics2?.lineMetrics?.length && !shiftLoading) setIsLoading(false);
13634
13647
  if (companySpecificMetricsTable.includes("unknown_company") && !error) {
13635
13648
  setError({ message: "Company ID not configured for metrics table.", code: "CONFIG_ERROR" });
@@ -13642,6 +13655,10 @@ var useDashboardMetrics = ({
13642
13655
  const usesShiftGroups = isFactory && shiftGroups.length > 0;
13643
13656
  const singleShiftDetails = usesShiftGroups ? null : shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(defaultTimezone, staticShiftConfig);
13644
13657
  const fetchKey = usesShiftGroups ? `factory|${companyId || "unknown"}|${shiftGroupsKey}` : isFactory ? `factory|${companyId || "unknown"}|${singleShiftDetails?.date}|${singleShiftDetails?.shiftId}|${targetLineIdsKey}` : `${currentLineIdToUse}|${companyId || "unknown"}|${singleShiftDetails?.date}|${singleShiftDetails?.shiftId}`;
13658
+ const responseScopeKey = buildMetricsScopeKey(
13659
+ currentLineIdToUse,
13660
+ isFactory ? targetLineIds : void 0
13661
+ );
13645
13662
  logDebug("[useDashboardMetrics] Fetch key details:", {
13646
13663
  isFactory,
13647
13664
  usesShiftGroups,
@@ -13651,14 +13668,8 @@ var useDashboardMetrics = ({
13651
13668
  singleShiftDetails,
13652
13669
  fetchKey
13653
13670
  });
13654
- if (inFlightFetchKeyRef.current === fetchKey) {
13655
- updateQueueRef.current = false;
13656
- return;
13657
- }
13658
- if (!force && lastFetchKeyRef.current === fetchKey) {
13659
- updateQueueRef.current = false;
13660
- return;
13661
- }
13671
+ if (inFlightFetchKeyRef.current === fetchKey) return;
13672
+ if (!force && lastFetchKeyRef.current === fetchKey) return;
13662
13673
  const requestId = ++activeRequestIdRef.current;
13663
13674
  const requestLineId = currentLineIdToUse;
13664
13675
  isFetchingRef.current = true;
@@ -13681,6 +13692,7 @@ var useDashboardMetrics = ({
13681
13692
  efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND
13682
13693
  });
13683
13694
  setMetricsLineId(requestLineId);
13695
+ setMetricsScopeKey(responseScopeKey);
13684
13696
  lastFetchKeyRef.current = inFlightFetchKeyRef.current;
13685
13697
  return;
13686
13698
  }
@@ -13890,6 +13902,7 @@ var useDashboardMetrics = ({
13890
13902
  hydrateFromBackend(idleTimeVlmByLine);
13891
13903
  setMetrics(newMetricsState);
13892
13904
  setMetricsLineId(requestLineId);
13905
+ setMetricsScopeKey(responseScopeKey);
13893
13906
  lastFetchKeyRef.current = inFlightFetchKeyRef.current;
13894
13907
  } catch (err) {
13895
13908
  if (abortController.signal.aborted || err?.name === "AbortError") {
@@ -13912,9 +13925,24 @@ var useDashboardMetrics = ({
13912
13925
  if (requestId === activeRequestIdRef.current) {
13913
13926
  setIsLoading(false);
13914
13927
  isFetchingRef.current = false;
13915
- updateQueueRef.current = false;
13916
13928
  activeFetchLineIdRef.current = null;
13917
13929
  inFlightFetchKeyRef.current = null;
13930
+ if (pendingRealtimeRefreshRef.current) {
13931
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
13932
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS - elapsedSinceLastRealtimeRefresh);
13933
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS, minIntervalRemaining);
13934
+ if (realtimeRefreshTimerRef.current === null) {
13935
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
13936
+ realtimeRefreshTimerRef.current = null;
13937
+ if (!pendingRealtimeRefreshRef.current || isFetchingRef.current || inFlightFetchKeyRef.current) {
13938
+ return;
13939
+ }
13940
+ pendingRealtimeRefreshRef.current = false;
13941
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
13942
+ fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13943
+ }, nextDelay);
13944
+ }
13945
+ }
13918
13946
  }
13919
13947
  }
13920
13948
  }, [
@@ -13944,20 +13972,59 @@ var useDashboardMetrics = ({
13944
13972
  React143.useEffect(() => {
13945
13973
  fetchAllMetricsRef.current = fetchAllMetrics;
13946
13974
  }, [fetchAllMetrics]);
13947
- const queueUpdate = React143.useCallback(() => {
13948
- if (updateQueueRef.current || !supabase) {
13949
- if (updateQueueRef.current) {
13950
- logDebug("[useDashboardMetrics] queueUpdate skipped: update already queued");
13975
+ const clearRealtimeRefreshTimer = React143.useCallback(() => {
13976
+ if (realtimeRefreshTimerRef.current !== null) {
13977
+ window.clearTimeout(realtimeRefreshTimerRef.current);
13978
+ realtimeRefreshTimerRef.current = null;
13979
+ }
13980
+ }, []);
13981
+ const scheduleRealtimeRefresh = React143.useCallback(() => {
13982
+ if (!enabled || !supabase) {
13983
+ pendingRealtimeRefreshRef.current = false;
13984
+ clearRealtimeRefreshTimer();
13985
+ return;
13986
+ }
13987
+ if (!pendingRealtimeRefreshRef.current || realtimeRefreshTimerRef.current !== null) {
13988
+ return;
13989
+ }
13990
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
13991
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS - elapsedSinceLastRealtimeRefresh);
13992
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS, minIntervalRemaining);
13993
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
13994
+ realtimeRefreshTimerRef.current = null;
13995
+ if (!pendingRealtimeRefreshRef.current) {
13996
+ return;
13951
13997
  }
13998
+ if (isFetchingRef.current || inFlightFetchKeyRef.current) {
13999
+ scheduleRealtimeRefresh();
14000
+ return;
14001
+ }
14002
+ pendingRealtimeRefreshRef.current = false;
14003
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
14004
+ fetchAllMetricsRef.current({ force: true, reason: "subscription" });
14005
+ }, nextDelay);
14006
+ }, [clearRealtimeRefreshTimer, enabled, supabase]);
14007
+ React143.useEffect(() => {
14008
+ return () => {
14009
+ clearRealtimeRefreshTimer();
14010
+ pendingRealtimeRefreshRef.current = false;
14011
+ };
14012
+ }, [clearRealtimeRefreshTimer]);
14013
+ const queueUpdate = React143.useCallback(() => {
14014
+ if (!enabled) {
14015
+ logDebug("[useDashboardMetrics] queueUpdate skipped: metrics disabled");
14016
+ return;
14017
+ }
14018
+ if (!supabase) {
13952
14019
  if (!supabase) {
13953
14020
  logDebug("[useDashboardMetrics] queueUpdate skipped: supabase not ready");
13954
14021
  }
13955
14022
  return;
13956
14023
  }
13957
- logDebug("[useDashboardMetrics] queueUpdate triggered from realtime");
13958
- updateQueueRef.current = true;
13959
- fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13960
- }, [supabase, enabled]);
14024
+ pendingRealtimeRefreshRef.current = true;
14025
+ logDebug("[useDashboardMetrics] queueUpdate queued realtime refresh");
14026
+ scheduleRealtimeRefresh();
14027
+ }, [enabled, scheduleRealtimeRefresh, supabase]);
13961
14028
  React143.useEffect(() => {
13962
14029
  if (enabled && lineId && supabase && !shiftLoading && !isTimezoneLoading) {
13963
14030
  fetchAllMetrics({ reason: "line-change" });
@@ -14077,7 +14144,7 @@ var useDashboardMetrics = ({
14077
14144
  { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
14078
14145
  (payload) => {
14079
14146
  const payloadData = payload.new || payload.old;
14080
- console.log("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
14147
+ logDebug("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
14081
14148
  eventType: payload.eventType,
14082
14149
  lineId: payloadData?.line_id,
14083
14150
  workspaceId: payloadData?.workspace_id,
@@ -14099,14 +14166,14 @@ var useDashboardMetrics = ({
14099
14166
  shiftId: payloadData?.shift_id
14100
14167
  });
14101
14168
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
14102
- console.log("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
14169
+ logDebug("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
14103
14170
  queueUpdate();
14104
14171
  } else {
14105
- console.log("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
14172
+ logDebug("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
14106
14173
  }
14107
14174
  }
14108
14175
  ).subscribe((status) => {
14109
- console.log("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
14176
+ logDebug("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
14110
14177
  channel: wsChannelName,
14111
14178
  status,
14112
14179
  table: companySpecificMetricsTable,
@@ -14124,7 +14191,7 @@ var useDashboardMetrics = ({
14124
14191
  { event: "*", schema, table: configuredLineMetricsTable, filter: filter2 },
14125
14192
  (payload) => {
14126
14193
  const payloadData = payload.new || payload.old;
14127
- console.log("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
14194
+ logDebug("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
14128
14195
  eventType: payload.eventType,
14129
14196
  lineId: payloadData?.line_id,
14130
14197
  date: payloadData?.date,
@@ -14144,15 +14211,15 @@ var useDashboardMetrics = ({
14144
14211
  shiftId: payloadData?.shift_id
14145
14212
  });
14146
14213
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
14147
- console.log("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
14214
+ logDebug("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
14148
14215
  queueUpdate();
14149
14216
  onLineMetricsUpdateRef.current?.();
14150
14217
  } else {
14151
- console.log("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
14218
+ logDebug("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
14152
14219
  }
14153
14220
  }
14154
14221
  ).subscribe((status) => {
14155
- console.log("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
14222
+ logDebug("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
14156
14223
  channel: lmChannelName,
14157
14224
  status,
14158
14225
  table: configuredLineMetricsTable,
@@ -14248,7 +14315,7 @@ var useDashboardMetrics = ({
14248
14315
  { event: "*", schema, table, filter: filter2 },
14249
14316
  (payload) => {
14250
14317
  const payloadData = payload.new || payload.old;
14251
- console.log(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
14318
+ logDebug(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
14252
14319
  eventType: payload.eventType,
14253
14320
  table: payload.table,
14254
14321
  lineId: payloadData?.line_id,
@@ -14272,14 +14339,14 @@ var useDashboardMetrics = ({
14272
14339
  shiftId: payloadData?.shift_id
14273
14340
  });
14274
14341
  if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
14275
- console.log(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
14342
+ logDebug(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
14276
14343
  callback();
14277
14344
  } else {
14278
- console.log(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
14345
+ logDebug(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
14279
14346
  }
14280
14347
  }
14281
14348
  ).subscribe((status) => {
14282
- console.log(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
14349
+ logDebug(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
14283
14350
  channel: channelName,
14284
14351
  status,
14285
14352
  table,
@@ -14328,14 +14395,17 @@ var useDashboardMetrics = ({
14328
14395
  lineId
14329
14396
  // NOTE: userAccessibleLineIds removed - accessed via ref
14330
14397
  ]);
14331
- const isMetricsForActiveLine = metricsLineId === lineId;
14332
- const safeMetrics = isMetricsForActiveLine ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
14398
+ const isCurrentScopeResolved = metricsScopeKey === requestedScopeKey;
14399
+ const hasLastGoodMetrics = metrics2.workspaceMetrics.length > 0 || metrics2.lineMetrics.length > 0;
14400
+ const canReuseLastGoodMetrics = hasLastGoodMetrics && !isCurrentScopeResolved && (isLoading || !!error);
14401
+ const safeMetrics = isCurrentScopeResolved || canReuseLastGoodMetrics ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
14333
14402
  return {
14334
14403
  workspaceMetrics: safeMetrics?.workspaceMetrics || [],
14335
14404
  lineMetrics: safeMetrics?.lineMetrics || [],
14336
14405
  efficiencyLegend: safeMetrics?.efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND,
14337
14406
  metadata: safeMetrics?.metadata,
14338
- isLoading: enabled ? isLoading || !isMetricsForActiveLine : false,
14407
+ isLoading: enabled ? isLoading || !isCurrentScopeResolved && !canReuseLastGoodMetrics : false,
14408
+ isCurrentScopeResolved,
14339
14409
  error,
14340
14410
  refetch: () => {
14341
14411
  if (!enabled) {
@@ -16783,6 +16853,7 @@ var isInitialized = false;
16783
16853
  var isInitializing = false;
16784
16854
  var initializedWithLineIds = [];
16785
16855
  var missingLineContextWarnings = /* @__PURE__ */ new Set();
16856
+ var lineLoadPromises = /* @__PURE__ */ new Map();
16786
16857
  var initializationPromise = null;
16787
16858
  var workspaceDisplayNamesListeners = /* @__PURE__ */ new Set();
16788
16859
  var notifyWorkspaceDisplayNamesListeners = (changedLineId) => {
@@ -16798,6 +16869,30 @@ var subscribeWorkspaceDisplayNames = (listener) => {
16798
16869
  workspaceDisplayNamesListeners.add(listener);
16799
16870
  return () => workspaceDisplayNamesListeners.delete(listener);
16800
16871
  };
16872
+ var storeLineDisplayNames = (lineId, lineDisplayNamesMap) => {
16873
+ runtimeWorkspaceDisplayNames[lineId] = {};
16874
+ lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16875
+ runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16876
+ });
16877
+ };
16878
+ var ensureLineWorkspaceDisplayNamesLoaded = async (lineId) => {
16879
+ if (!lineId || runtimeWorkspaceDisplayNames[lineId]) {
16880
+ return;
16881
+ }
16882
+ const existingPromise = lineLoadPromises.get(lineId);
16883
+ if (existingPromise) {
16884
+ await existingPromise;
16885
+ return;
16886
+ }
16887
+ const loadPromise = workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16888
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
16889
+ notifyWorkspaceDisplayNamesListeners(lineId);
16890
+ }).finally(() => {
16891
+ lineLoadPromises.delete(lineId);
16892
+ });
16893
+ lineLoadPromises.set(lineId, loadPromise);
16894
+ await loadPromise;
16895
+ };
16801
16896
  var getAllWorkspaceDisplayNamesSnapshot = (lineId) => {
16802
16897
  if (lineId && runtimeWorkspaceDisplayNames[lineId]) {
16803
16898
  return { ...runtimeWorkspaceDisplayNames[lineId] };
@@ -16862,6 +16957,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
16862
16957
  }
16863
16958
  console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
16864
16959
  runtimeWorkspaceDisplayNames = {};
16960
+ lineLoadPromises.clear();
16865
16961
  if (targetLineIds.length > 0) {
16866
16962
  const results = await Promise.all(
16867
16963
  targetLineIds.map(async (lineId) => {
@@ -16871,10 +16967,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
16871
16967
  })
16872
16968
  );
16873
16969
  results.forEach(({ lineId, lineDisplayNamesMap }) => {
16874
- runtimeWorkspaceDisplayNames[lineId] = {};
16875
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16876
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16877
- });
16970
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
16878
16971
  console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16879
16972
  });
16880
16973
  } else {
@@ -16906,13 +16999,8 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
16906
16999
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16907
17000
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
16908
17001
  try {
16909
- const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
16910
- runtimeWorkspaceDisplayNames[lineId] = {};
16911
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16912
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16913
- });
16914
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16915
- notifyWorkspaceDisplayNamesListeners(lineId);
17002
+ await ensureLineWorkspaceDisplayNamesLoaded(lineId);
17003
+ console.log(`\u2705 Added workspaces for line ${lineId}`);
16916
17004
  } catch (error) {
16917
17005
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16918
17006
  }
@@ -16925,13 +17013,8 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
16925
17013
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16926
17014
  console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
16927
17015
  try {
16928
- const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
16929
- runtimeWorkspaceDisplayNames[lineId] = {};
16930
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16931
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16932
- });
16933
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16934
- notifyWorkspaceDisplayNamesListeners(lineId);
17016
+ await ensureLineWorkspaceDisplayNamesLoaded(lineId);
17017
+ console.log(`\u2705 Added workspaces for line ${lineId}`);
16935
17018
  } catch (error) {
16936
17019
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16937
17020
  }
@@ -16968,13 +17051,8 @@ var getWorkspaceDisplayName = (workspaceId, lineId) => {
16968
17051
  }
16969
17052
  if (isInitialized && lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16970
17053
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching its workspaces...`);
16971
- workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16972
- runtimeWorkspaceDisplayNames[lineId] = {};
16973
- lineDisplayNamesMap.forEach((displayName2, workspaceId2) => {
16974
- runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
16975
- });
16976
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
16977
- notifyWorkspaceDisplayNamesListeners(lineId);
17054
+ ensureLineWorkspaceDisplayNamesLoaded(lineId).then(() => {
17055
+ console.log(`\u2705 Added workspaces for line ${lineId} to cache`);
16978
17056
  }).catch((error) => {
16979
17057
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16980
17058
  });
@@ -17016,13 +17094,8 @@ var getShortWorkspaceDisplayName = (workspaceId, lineId) => {
17016
17094
  }
17017
17095
  if (isInitialized && lineId && !runtimeWorkspaceDisplayNames[lineId]) {
17018
17096
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching its workspaces...`);
17019
- workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
17020
- runtimeWorkspaceDisplayNames[lineId] = {};
17021
- lineDisplayNamesMap.forEach((displayName2, workspaceId2) => {
17022
- runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
17023
- });
17024
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
17025
- notifyWorkspaceDisplayNamesListeners(lineId);
17097
+ ensureLineWorkspaceDisplayNamesLoaded(lineId).then(() => {
17098
+ console.log(`\u2705 Added workspaces for line ${lineId} to cache`);
17026
17099
  }).catch((error) => {
17027
17100
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
17028
17101
  });
@@ -17102,6 +17175,7 @@ var refreshWorkspaceDisplayNames = async (companyId) => {
17102
17175
  workspaceService.clearWorkspaceDisplayNamesCache();
17103
17176
  runtimeWorkspaceDisplayNames = {};
17104
17177
  isInitialized = false;
17178
+ lineLoadPromises.clear();
17105
17179
  await initializeWorkspaceDisplayNames();
17106
17180
  notifyWorkspaceDisplayNamesListeners();
17107
17181
  };
@@ -17111,6 +17185,7 @@ var clearWorkspaceDisplayNamesCache = () => {
17111
17185
  isInitialized = false;
17112
17186
  isInitializing = false;
17113
17187
  initializedWithLineIds = [];
17188
+ lineLoadPromises.clear();
17114
17189
  initializationPromise = null;
17115
17190
  notifyWorkspaceDisplayNamesListeners();
17116
17191
  };
@@ -17240,7 +17315,16 @@ var useWorkspaceDisplayNamesMap = (workspaceIds, lineId, companyId) => {
17240
17315
  refetch: fetchDisplayNames
17241
17316
  };
17242
17317
  };
17243
- var useWorkspaceVideoStreams = (workspaces) => {
17318
+ var useWorkspaceVideoStreams = (workspaces, options = {}) => {
17319
+ const explicitLineIdsKey = React143.useMemo(() => {
17320
+ const ids = /* @__PURE__ */ new Set();
17321
+ for (const lineId of options.lineIds || []) {
17322
+ if (lineId) {
17323
+ ids.add(lineId);
17324
+ }
17325
+ }
17326
+ return Array.from(ids).sort().join(",");
17327
+ }, [options.lineIds]);
17244
17328
  const workspaceIdsKey = React143.useMemo(() => {
17245
17329
  const ids = /* @__PURE__ */ new Set();
17246
17330
  for (const workspace of workspaces) {
@@ -17250,7 +17334,7 @@ var useWorkspaceVideoStreams = (workspaces) => {
17250
17334
  }
17251
17335
  return Array.from(ids).sort().join(",");
17252
17336
  }, [workspaces]);
17253
- const lineIdsKey = React143.useMemo(() => {
17337
+ const inferredLineIdsKey = React143.useMemo(() => {
17254
17338
  const ids = /* @__PURE__ */ new Set();
17255
17339
  for (const workspace of workspaces) {
17256
17340
  if (workspace.line_id) {
@@ -17259,18 +17343,19 @@ var useWorkspaceVideoStreams = (workspaces) => {
17259
17343
  }
17260
17344
  return Array.from(ids).sort().join(",");
17261
17345
  }, [workspaces]);
17346
+ const lineIdsKey = explicitLineIdsKey || inferredLineIdsKey;
17262
17347
  const requestKey = React143.useMemo(() => {
17263
- if (workspaceIdsKey) {
17264
- return `workspaces:${workspaceIdsKey}`;
17265
- }
17266
17348
  if (lineIdsKey) {
17267
17349
  return `lines:${lineIdsKey}`;
17268
17350
  }
17351
+ if (workspaceIdsKey) {
17352
+ return `workspaces:${workspaceIdsKey}`;
17353
+ }
17269
17354
  return "";
17270
17355
  }, [workspaceIdsKey, lineIdsKey]);
17271
17356
  const workspaceIds = React143.useMemo(
17272
- () => workspaceIdsKey ? workspaceIdsKey.split(",") : [],
17273
- [workspaceIdsKey]
17357
+ () => lineIdsKey ? [] : workspaceIdsKey ? workspaceIdsKey.split(",") : [],
17358
+ [lineIdsKey, workspaceIdsKey]
17274
17359
  );
17275
17360
  const lineIds = React143.useMemo(
17276
17361
  () => lineIdsKey ? lineIdsKey.split(",") : [],
@@ -18814,6 +18899,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18814
18899
  const supabase = useSupabase();
18815
18900
  const databaseConfig = useDatabaseConfig();
18816
18901
  const refreshInterval = options.refreshInterval ?? DEFAULT_REFRESH_INTERVAL_MS;
18902
+ const enabled = options.enabled ?? true;
18817
18903
  const workspaceIdsKey = React143.useMemo(() => {
18818
18904
  const ids = Array.from(new Set(workspaceIds.filter(Boolean)));
18819
18905
  return ids.sort().join(",");
@@ -18824,7 +18910,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18824
18910
  const isFetchingRef = React143.useRef(false);
18825
18911
  const refreshIntervalRef = React143.useRef(null);
18826
18912
  const fetchLastSeen = React143.useCallback(async () => {
18827
- if (!supabase || !workspaceIdsKey || isFetchingRef.current) return;
18913
+ if (!enabled || !supabase || !workspaceIdsKey || isFetchingRef.current) return;
18828
18914
  const healthTable = databaseConfig?.tables?.workspace_health || "workspace_health_status";
18829
18915
  try {
18830
18916
  isFetchingRef.current = true;
@@ -18856,12 +18942,18 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18856
18942
  setIsLoading(false);
18857
18943
  isFetchingRef.current = false;
18858
18944
  }
18859
- }, [supabase, workspaceIdsKey, databaseConfig?.tables?.workspace_health]);
18945
+ }, [databaseConfig?.tables?.workspace_health, enabled, supabase, workspaceIdsKey]);
18860
18946
  React143.useEffect(() => {
18947
+ if (!enabled) {
18948
+ setIsLoading(false);
18949
+ setError(null);
18950
+ setLastSeenByWorkspaceId({});
18951
+ return;
18952
+ }
18861
18953
  fetchLastSeen();
18862
- }, [fetchLastSeen]);
18954
+ }, [enabled, fetchLastSeen]);
18863
18955
  React143.useEffect(() => {
18864
- if (!refreshInterval || !workspaceIdsKey) return;
18956
+ if (!enabled || !refreshInterval || !workspaceIdsKey) return;
18865
18957
  refreshIntervalRef.current = setInterval(() => {
18866
18958
  fetchLastSeen();
18867
18959
  }, refreshInterval);
@@ -18871,7 +18963,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18871
18963
  refreshIntervalRef.current = null;
18872
18964
  }
18873
18965
  };
18874
- }, [fetchLastSeen, refreshInterval, workspaceIdsKey]);
18966
+ }, [enabled, fetchLastSeen, refreshInterval, workspaceIdsKey]);
18875
18967
  return {
18876
18968
  lastSeenByWorkspaceId,
18877
18969
  isLoading,
@@ -34704,7 +34796,7 @@ var VideoCard = React143__namespace.default.memo(({
34704
34796
  });
34705
34797
  const showOffline = Boolean(isStreamStale);
34706
34798
  const lastSeenText = lastSeenLabel || "Unknown";
34707
- const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
34799
+ const workspaceDisplayName = displayName || workspace.displayName || workspace.workspace_name;
34708
34800
  const videoGridMetricValue = getVideoGridMetricValue(workspace);
34709
34801
  const videoGridDisplayValue = getVideoGridDisplayValue(workspace, effectiveLegend, displayMinuteBucket);
34710
34802
  const videoGridColorState = getVideoGridColorState(workspace, effectiveLegend);
@@ -34917,7 +35009,10 @@ var VideoGridView = React143__namespace.default.memo(({
34917
35009
  }
34918
35010
  return Array.from(ids);
34919
35011
  }, [workspaces]);
34920
- const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds);
35012
+ const healthFetchEnabled = !videoStreamsLoading && workspaces.length > 0;
35013
+ const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds, {
35014
+ enabled: healthFetchEnabled
35015
+ });
34921
35016
  React143.useEffect(() => {
34922
35017
  const sample = workspaces.slice(0, 3).map((workspace) => ({
34923
35018
  id: workspace.workspace_uuid || workspace.workspace_name,
@@ -35016,6 +35111,9 @@ var VideoGridView = React143__namespace.default.memo(({
35016
35111
  }, [sortedWorkspaces, lineOrder, lineNames]);
35017
35112
  lineGroups.length > 1;
35018
35113
  const streamsReady = !videoStreamsLoading;
35114
+ const resolveWorkspaceDisplayName = React143.useCallback((workspace) => {
35115
+ return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
35116
+ }, [displayNames]);
35019
35117
  const calculateOptimalGrid = React143.useCallback(() => {
35020
35118
  if (!containerRef.current) return;
35021
35119
  const containerPadding = 16;
@@ -35146,11 +35244,11 @@ var VideoGridView = React143__namespace.default.memo(({
35146
35244
  efficiency: workspace.efficiency,
35147
35245
  action_count: workspace.action_count
35148
35246
  });
35149
- const displayName = workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
35247
+ const displayName = resolveWorkspaceDisplayName(workspace);
35150
35248
  const currentPath = (router$1.asPath || "/").split("#")[0];
35151
35249
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id, currentPath);
35152
35250
  router$1.push(`/workspace/${workspaceId}${navParams}`);
35153
- }, [router$1, prewarmClipsInit]);
35251
+ }, [resolveWorkspaceDisplayName, router$1, prewarmClipsInit]);
35154
35252
  const handleStreamError = React143.useCallback((workspaceId, options) => {
35155
35253
  const isR2Stream = options?.isR2Stream ?? false;
35156
35254
  const hasFallback = Boolean(options?.fallbackUrl);
@@ -35259,7 +35357,7 @@ var VideoGridView = React143__namespace.default.memo(({
35259
35357
  legend: effectiveLegend,
35260
35358
  cropping: card.workspaceCropping,
35261
35359
  canvasFps: effectiveCanvasFps,
35262
- displayName: displayNames[`${card.workspace.line_id}_${card.workspace.workspace_name}`] || getWorkspaceDisplayName(card.workspace.workspace_name, card.workspace.line_id),
35360
+ displayName: resolveWorkspaceDisplayName(card.workspace),
35263
35361
  lastSeenLabel: card.lastSeenLabel,
35264
35362
  useRAF: effectiveUseRAF,
35265
35363
  displayMinuteBucket,
@@ -35271,16 +35369,15 @@ var VideoGridView = React143__namespace.default.memo(({
35271
35369
  },
35272
35370
  card.workspaceKey
35273
35371
  ), [
35274
- displayNames,
35275
35372
  displayMinuteBucket,
35276
35373
  effectiveCanvasFps,
35277
35374
  effectiveLegend,
35278
35375
  effectiveUseRAF,
35279
- getWorkspaceDisplayName,
35280
35376
  handleStreamError,
35281
35377
  handleWorkspaceClick,
35282
35378
  onWorkspaceHover,
35283
35379
  onWorkspaceHoverEnd,
35380
+ resolveWorkspaceDisplayName,
35284
35381
  selectedLine
35285
35382
  ]);
35286
35383
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `relative overflow-hidden h-full w-full bg-slate-50/30 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -35366,6 +35463,9 @@ var MapGridView = React143__namespace.default.memo(({
35366
35463
  prewarmInFlightRef.current.delete(workspaceId);
35367
35464
  });
35368
35465
  }, [dashboardConfig, timezone, supabase]);
35466
+ const resolveWorkspaceDisplayName = React143.useCallback((workspace) => {
35467
+ return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
35468
+ }, [displayNames]);
35369
35469
  const handleWorkspaceClick = React143.useCallback((workspace) => {
35370
35470
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
35371
35471
  prewarmClipsInit(workspace);
@@ -35377,12 +35477,11 @@ var MapGridView = React143__namespace.default.memo(({
35377
35477
  efficiency: workspace.efficiency,
35378
35478
  action_count: workspace.action_count
35379
35479
  });
35380
- const displayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
35381
- getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
35480
+ const displayName = resolveWorkspaceDisplayName(workspace);
35382
35481
  const currentPath = (router$1.asPath || "/").split("#")[0];
35383
35482
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id, currentPath);
35384
35483
  router$1.push(`/workspace/${workspaceId}${navParams}`);
35385
- }, [router$1, displayNames, prewarmClipsInit]);
35484
+ }, [resolveWorkspaceDisplayName, router$1, prewarmClipsInit]);
35386
35485
  const activePositions = React143.useMemo(() => {
35387
35486
  return workspacePositions.filter((pos) => {
35388
35487
  const wsKey = pos.id.toUpperCase();
@@ -35412,7 +35511,18 @@ var MapGridView = React143__namespace.default.memo(({
35412
35511
  workspace_uuid: position.id,
35413
35512
  // Use config ID as temporary UUID
35414
35513
  line_id: "",
35514
+ // Empty line ID
35515
+ company_id: "",
35516
+ shift_id: 0,
35517
+ date: "",
35415
35518
  efficiency: 0,
35519
+ performance_score: 0,
35520
+ action_count: 0,
35521
+ pph: 0,
35522
+ avg_cycle_time: 0,
35523
+ trend: 0,
35524
+ predicted_output: 0,
35525
+ action_threshold: 0,
35416
35526
  show_exclamation: false
35417
35527
  };
35418
35528
  const workspaceId = effectiveWorkspace.workspace_uuid || effectiveWorkspace.workspace_name;
@@ -35421,7 +35531,7 @@ var MapGridView = React143__namespace.default.memo(({
35421
35531
  const performanceColor = isInactivePlaceholder || isConveyorRow ? "bg-slate-200 border-slate-300 text-slate-500" : getPerformanceColor(effectiveWorkspace.efficiency);
35422
35532
  !isInactivePlaceholder && !isConveyorRow && (effectiveWorkspace.show_exclamation ?? (effectiveWorkspace.efficiency < 50 && effectiveWorkspace.efficiency >= 10));
35423
35533
  const configLabel = position.label;
35424
- const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : displayNames[`${effectiveWorkspace.line_id}_${effectiveWorkspace.workspace_name}`] || getWorkspaceDisplayName(effectiveWorkspace.workspace_name, effectiveWorkspace.line_id) || position.id);
35534
+ const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : resolveWorkspaceDisplayName(effectiveWorkspace) || position.id);
35425
35535
  return /* @__PURE__ */ jsxRuntime.jsx(
35426
35536
  motion.div,
35427
35537
  {
@@ -39388,9 +39498,12 @@ SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
39388
39498
  var LoadingOverlay = ({
39389
39499
  isVisible,
39390
39500
  message = "Loading...",
39391
- className
39501
+ className,
39502
+ contentClassName,
39503
+ contentVariant = "card"
39392
39504
  }) => {
39393
39505
  if (!isVisible) return null;
39506
+ 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";
39394
39507
  return /* @__PURE__ */ jsxRuntime.jsx(
39395
39508
  motion.div,
39396
39509
  {
@@ -39401,7 +39514,7 @@ var LoadingOverlay = ({
39401
39514
  className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
39402
39515
  "aria-modal": "true",
39403
39516
  role: "dialog",
39404
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message }) })
39517
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${contentClasses} ${contentClassName || ""}`, children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message }) })
39405
39518
  }
39406
39519
  );
39407
39520
  };
@@ -60731,6 +60844,353 @@ var HelpView = ({
60731
60844
  };
60732
60845
  var AuthenticatedHelpView = withAuth(HelpView);
60733
60846
  var HelpView_default = HelpView;
60847
+ var sortWorkspaceMetrics = (left, right) => {
60848
+ if (left.line_id !== right.line_id) {
60849
+ return left.line_id.localeCompare(right.line_id);
60850
+ }
60851
+ return left.workspace_name.localeCompare(right.workspace_name, void 0, { numeric: true });
60852
+ };
60853
+ var transformWorkspaceMetrics = (rows) => (rows || []).map((item) => {
60854
+ const actionFamily = normalizeActionFamily({
60855
+ actionFamily: item.action_family,
60856
+ actionType: item.action_type,
60857
+ actionName: item.action_name
60858
+ });
60859
+ const actionType = actionFamily === "assembly" || actionFamily === "output" ? actionFamily : null;
60860
+ const metric = {
60861
+ company_id: item.company_id || "",
60862
+ line_id: item.line_id,
60863
+ shift_id: item.shift_id,
60864
+ date: item.date,
60865
+ workspace_uuid: item.workspace_id,
60866
+ workspace_name: item.workspace_name,
60867
+ displayName: item.workspace_display_name || item.display_name || void 0,
60868
+ action_count: item.total_output || 0,
60869
+ pph: item.avg_pph || 0,
60870
+ performance_score: item.performance_score || 0,
60871
+ avg_cycle_time: item.avg_cycle_time || 0,
60872
+ ideal_cycle_time: item.ideal_cycle_time || void 0,
60873
+ trend: item.trend_score === 1 ? 2 : 0,
60874
+ predicted_output: item.ideal_output || 0,
60875
+ efficiency: item.efficiency || 0,
60876
+ action_threshold: item.total_day_output || 0,
60877
+ monitoring_mode: item.monitoring_mode ?? void 0,
60878
+ idle_time: typeof item.idle_time === "number" ? item.idle_time : Number(item.idle_time),
60879
+ idle_time_hourly: item.idle_time_hourly ?? null,
60880
+ shift_start: item.shift_start ?? void 0,
60881
+ shift_end: item.shift_end ?? void 0,
60882
+ assembly_enabled: item.assembly_enabled ?? false,
60883
+ video_grid_metric_mode: normalizeVideoGridMetricMode(
60884
+ item.video_grid_metric_mode,
60885
+ item.assembly_enabled ?? false
60886
+ ),
60887
+ action_type: actionType,
60888
+ action_family: actionFamily,
60889
+ action_display_name: getActionDisplayName({
60890
+ displayName: item.action_display_name,
60891
+ actionFamily: item.action_family,
60892
+ actionType: item.action_type,
60893
+ actionName: item.action_name
60894
+ }),
60895
+ recent_flow_percent: item.recent_flow_percent ?? null,
60896
+ recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
60897
+ recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
60898
+ recent_flow_computed_at: item.recent_flow_computed_at ?? null,
60899
+ scheduled_break_active: item.scheduled_break_active ?? false,
60900
+ incoming_wip_current: item.incoming_wip_current ?? null,
60901
+ incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
60902
+ incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null,
60903
+ show_exclamation: item.show_exclamation ?? void 0
60904
+ };
60905
+ workspaceMetricsStore.setOverview(metric);
60906
+ return metric;
60907
+ }).sort(sortWorkspaceMetrics);
60908
+ var transformActiveBreaks = (activeBreaksByLine) => {
60909
+ if (!activeBreaksByLine) return [];
60910
+ return Object.values(activeBreaksByLine).flat().map((item) => ({
60911
+ lineId: item.line_id,
60912
+ shiftName: item.shift_name || "",
60913
+ startTime: item.start_time,
60914
+ endTime: item.end_time,
60915
+ duration: item.duration || 0,
60916
+ remarks: item.remarks || "",
60917
+ elapsedMinutes: item.elapsed_minutes || 0,
60918
+ remainingMinutes: item.remaining_minutes || 0
60919
+ })).sort((left, right) => {
60920
+ if (left.lineId !== right.lineId) return left.lineId.localeCompare(right.lineId);
60921
+ if (left.shiftName !== right.shiftName) return left.shiftName.localeCompare(right.shiftName);
60922
+ return left.startTime.localeCompare(right.startTime);
60923
+ });
60924
+ };
60925
+ var createEmptyState = () => ({
60926
+ requestKey: null,
60927
+ resolvedScope: [],
60928
+ scopeKey: null,
60929
+ lines: [],
60930
+ workspaceMetrics: [],
60931
+ lineMetrics: [],
60932
+ kpiTrend: null,
60933
+ activeBreaks: [],
60934
+ videoStreamsByWorkspaceId: {},
60935
+ efficiencyLegend: null,
60936
+ metadata: {}
60937
+ });
60938
+ var normalizeMetadata = (metadata) => ({
60939
+ hasFlowBuffers: Boolean(metadata?.has_flow_buffers),
60940
+ idleTimeVlmByLine: metadata?.idle_time_vlm_by_line ?? {},
60941
+ generatedAt: metadata?.generated_at,
60942
+ cacheStatus: metadata?.cache_status,
60943
+ warnings: metadata?.warnings ?? []
60944
+ });
60945
+ var useLiveMonitorBootstrap = ({
60946
+ lineIds,
60947
+ companyId,
60948
+ enabled = true
60949
+ }) => {
60950
+ const supabase = useSupabase();
60951
+ const entityConfig = useEntityConfig();
60952
+ const { hydrateFromBackend } = useIdleTimeVlmConfig();
60953
+ const resolvedCompanyId = companyId || entityConfig?.companyId;
60954
+ const rawLineIdsKey = (lineIds || []).filter(Boolean).join(",");
60955
+ const normalizedLineIds = React143.useMemo(
60956
+ () => Array.from(new Set(rawLineIdsKey ? rawLineIdsKey.split(",") : [])),
60957
+ [rawLineIdsKey]
60958
+ );
60959
+ const requestKey = React143.useMemo(
60960
+ () => normalizedLineIds.slice().sort().join(","),
60961
+ [normalizedLineIds]
60962
+ );
60963
+ const [state, setState] = React143.useState(() => createEmptyState());
60964
+ const [isLoading, setIsLoading] = React143.useState(false);
60965
+ const [error, setError] = React143.useState(null);
60966
+ const activeRequestIdRef = React143.useRef(0);
60967
+ const fetchBootstrap = React143.useCallback(async (force = false) => {
60968
+ if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
60969
+ return;
60970
+ }
60971
+ const requestId = ++activeRequestIdRef.current;
60972
+ setIsLoading(true);
60973
+ setError(null);
60974
+ try {
60975
+ const searchParams = new URLSearchParams();
60976
+ searchParams.set("company_id", resolvedCompanyId);
60977
+ searchParams.set("line_ids", normalizedLineIds.join(","));
60978
+ if (force) {
60979
+ searchParams.set("force_refresh", "true");
60980
+ }
60981
+ const response = await fetchBackendJson(
60982
+ supabase,
60983
+ `/api/dashboard/monitor-bootstrap?${searchParams.toString()}`,
60984
+ {
60985
+ method: "GET",
60986
+ timeoutMs: 2e4,
60987
+ retries: 0,
60988
+ dedupeKey: `monitor-bootstrap::${requestKey}::${force ? "force" : "cached"}`
60989
+ }
60990
+ );
60991
+ if (requestId !== activeRequestIdRef.current) {
60992
+ return;
60993
+ }
60994
+ const workspaceMetrics = transformWorkspaceMetrics(response.workspace_metrics || []);
60995
+ const activeBreaks = transformActiveBreaks(response.active_breaks_by_line);
60996
+ const metadata = normalizeMetadata(response.metadata);
60997
+ if (metadata.idleTimeVlmByLine) {
60998
+ hydrateFromBackend(metadata.idleTimeVlmByLine);
60999
+ }
61000
+ setState({
61001
+ requestKey,
61002
+ resolvedScope: response.resolved_scope || [],
61003
+ scopeKey: response.scope_key || null,
61004
+ lines: response.lines || [],
61005
+ workspaceMetrics,
61006
+ lineMetrics: response.line_metrics || [],
61007
+ kpiTrend: response.kpi_trend || null,
61008
+ activeBreaks,
61009
+ videoStreamsByWorkspaceId: response.video_streams_by_workspace_id || {},
61010
+ efficiencyLegend: response.efficiency_legend || null,
61011
+ metadata
61012
+ });
61013
+ } catch (fetchError) {
61014
+ if (requestId !== activeRequestIdRef.current) {
61015
+ return;
61016
+ }
61017
+ setError(fetchError instanceof Error ? fetchError : new Error("Failed to load live monitor bootstrap"));
61018
+ } finally {
61019
+ if (requestId === activeRequestIdRef.current) {
61020
+ setIsLoading(false);
61021
+ }
61022
+ }
61023
+ }, [enabled, supabase, resolvedCompanyId, normalizedLineIds, requestKey, hydrateFromBackend]);
61024
+ React143.useEffect(() => {
61025
+ if (!enabled) {
61026
+ setIsLoading(false);
61027
+ return;
61028
+ }
61029
+ if (!resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
61030
+ setState(createEmptyState());
61031
+ setIsLoading(false);
61032
+ setError(null);
61033
+ return;
61034
+ }
61035
+ void fetchBootstrap(false);
61036
+ }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
61037
+ React143.useEffect(() => {
61038
+ if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
61039
+ return void 0;
61040
+ }
61041
+ let intervalId = null;
61042
+ let timeoutId = null;
61043
+ const runMinuteTick = () => {
61044
+ void fetchBootstrap(true);
61045
+ };
61046
+ const startInterval = () => {
61047
+ runMinuteTick();
61048
+ intervalId = window.setInterval(runMinuteTick, 6e4);
61049
+ };
61050
+ const msUntilNextMinute = (() => {
61051
+ const remainder = Date.now() % 6e4;
61052
+ return remainder === 0 ? 6e4 : 6e4 - remainder;
61053
+ })();
61054
+ timeoutId = window.setTimeout(startInterval, msUntilNextMinute);
61055
+ return () => {
61056
+ if (timeoutId !== null) {
61057
+ window.clearTimeout(timeoutId);
61058
+ }
61059
+ if (intervalId !== null) {
61060
+ window.clearInterval(intervalId);
61061
+ }
61062
+ };
61063
+ }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
61064
+ const isCurrentScopeResolved = state.requestKey === requestKey;
61065
+ return {
61066
+ resolvedScope: state.resolvedScope,
61067
+ scopeKey: state.scopeKey,
61068
+ lines: state.lines,
61069
+ workspaceMetrics: state.workspaceMetrics,
61070
+ lineMetrics: state.lineMetrics,
61071
+ kpiTrend: state.kpiTrend,
61072
+ activeBreaks: state.activeBreaks,
61073
+ videoStreamsByWorkspaceId: state.videoStreamsByWorkspaceId,
61074
+ efficiencyLegend: state.efficiencyLegend,
61075
+ metadata: state.metadata,
61076
+ isLoading: enabled ? isLoading || !isCurrentScopeResolved : false,
61077
+ isCurrentScopeResolved,
61078
+ error,
61079
+ refetch: () => fetchBootstrap(true)
61080
+ };
61081
+ };
61082
+
61083
+ // src/lib/utils/liveMonitorBootstrap.ts
61084
+ var toNumber3 = (value) => {
61085
+ if (typeof value === "number" && Number.isFinite(value)) return value;
61086
+ if (typeof value === "string" && value.trim() !== "") {
61087
+ const parsed = Number(value);
61088
+ return Number.isFinite(parsed) ? parsed : 0;
61089
+ }
61090
+ return 0;
61091
+ };
61092
+ var normalizeNullableNumber = (value) => {
61093
+ if (value === null || value === void 0 || value === "") return null;
61094
+ return toNumber3(value);
61095
+ };
61096
+ var getLiveMonitorBootstrapMode = () => {
61097
+ const rawMode = (process.env.NEXT_PUBLIC_MONITOR_BOOTSTRAP_MODE || "").trim().toLowerCase();
61098
+ if (rawMode === "legacy" || rawMode === "shadow" || rawMode === "bootstrap") {
61099
+ return rawMode;
61100
+ }
61101
+ return "bootstrap";
61102
+ };
61103
+ var normalizeMonitorShadowSnapshot = (input) => {
61104
+ const sortedSelectedLineIds = Array.from(new Set((input.selectedLineIds || []).filter(Boolean))).sort();
61105
+ const resolvedScope = (input.resolvedScope || []).filter((entry) => entry?.line_id && entry?.date && entry?.shift_id !== void 0 && entry?.shift_id !== null).map((entry) => ({
61106
+ line_id: entry.line_id,
61107
+ date: entry.date,
61108
+ shift_id: entry.shift_id
61109
+ })).sort((left, right) => {
61110
+ if (left.line_id !== right.line_id) return left.line_id.localeCompare(right.line_id);
61111
+ if (left.date !== right.date) return left.date.localeCompare(right.date);
61112
+ return left.shift_id - right.shift_id;
61113
+ });
61114
+ const lines = (input.lines || []).filter((entry) => entry?.line_id).map((entry) => ({
61115
+ line_id: entry.line_id,
61116
+ line_name: entry.line_name || "Unknown Line"
61117
+ })).sort((left, right) => left.line_id.localeCompare(right.line_id));
61118
+ const workspaces = (input.workspaces || []).map((workspace) => {
61119
+ const workspaceId = workspace.workspace_uuid || null;
61120
+ const stream = workspaceId ? input.videoStreamsByWorkspaceId?.[workspaceId] || null : null;
61121
+ return {
61122
+ workspace_id: workspaceId,
61123
+ line_id: workspace.line_id,
61124
+ workspace_name: workspace.workspace_name,
61125
+ display_name: workspace.displayName || null,
61126
+ efficiency: toNumber3(workspace.efficiency),
61127
+ trend: toNumber3(workspace.trend),
61128
+ show_exclamation: Boolean(workspace.show_exclamation),
61129
+ scheduled_break_active: Boolean(workspace.scheduled_break_active),
61130
+ action_count: toNumber3(workspace.action_count),
61131
+ action_threshold: toNumber3(workspace.action_threshold),
61132
+ predicted_output: toNumber3(workspace.predicted_output),
61133
+ avg_cycle_time: toNumber3(workspace.avg_cycle_time),
61134
+ pph: toNumber3(workspace.pph),
61135
+ recent_flow_percent: normalizeNullableNumber(workspace.recent_flow_percent),
61136
+ incoming_wip_current: normalizeNullableNumber(workspace.incoming_wip_current),
61137
+ video_stream: stream ? {
61138
+ workspace_id: stream.workspace_id,
61139
+ camera_uuid: stream.camera_uuid,
61140
+ stream_camera_uuid: stream.stream_camera_uuid,
61141
+ hls_url: stream.hls_url,
61142
+ crop: stream.crop
61143
+ } : null
61144
+ };
61145
+ }).sort((left, right) => {
61146
+ if (left.line_id !== right.line_id) return left.line_id.localeCompare(right.line_id);
61147
+ return left.workspace_name.localeCompare(right.workspace_name, void 0, { numeric: true });
61148
+ });
61149
+ const activeBreaks = (input.activeBreaks || []).map((activeBreak) => ({
61150
+ lineId: activeBreak.lineId,
61151
+ shiftName: activeBreak.shiftName,
61152
+ startTime: activeBreak.startTime,
61153
+ endTime: activeBreak.endTime,
61154
+ remainingMinutes: toNumber3(activeBreak.remainingMinutes)
61155
+ })).sort((left, right) => {
61156
+ if (left.lineId !== right.lineId) return left.lineId.localeCompare(right.lineId);
61157
+ if (left.shiftName !== right.shiftName) return left.shiftName.localeCompare(right.shiftName);
61158
+ return left.startTime.localeCompare(right.startTime);
61159
+ });
61160
+ return {
61161
+ selectedLineIds: sortedSelectedLineIds,
61162
+ resolvedScope,
61163
+ lines,
61164
+ workspaces,
61165
+ activeBreaks,
61166
+ kpis: input.kpis || null,
61167
+ kpiTrend: input.kpiTrend || null,
61168
+ efficiencyLegend: input.efficiencyLegend || null
61169
+ };
61170
+ };
61171
+ var diffMonitorShadowSnapshots = (legacySnapshot, bootstrapSnapshot) => {
61172
+ const mismatches = [];
61173
+ const compareSection = (section) => {
61174
+ const legacyValue = legacySnapshot[section];
61175
+ const bootstrapValue = bootstrapSnapshot[section];
61176
+ if (JSON.stringify(legacyValue) !== JSON.stringify(bootstrapValue)) {
61177
+ mismatches.push({
61178
+ section,
61179
+ legacy: legacyValue,
61180
+ bootstrap: bootstrapValue
61181
+ });
61182
+ }
61183
+ };
61184
+ compareSection("selectedLineIds");
61185
+ compareSection("resolvedScope");
61186
+ compareSection("lines");
61187
+ compareSection("workspaces");
61188
+ compareSection("activeBreaks");
61189
+ compareSection("kpis");
61190
+ compareSection("kpiTrend");
61191
+ compareSection("efficiencyLegend");
61192
+ return mismatches;
61193
+ };
60734
61194
 
60735
61195
  // src/lib/services/notificationService.ts
60736
61196
  var API_BASE_URL = process.env.NEXT_PUBLIC_BACKEND_URL;
@@ -60872,7 +61332,10 @@ var logDebug3 = (...args) => {
60872
61332
  if (!DEBUG_DASHBOARD_LOGS3) return;
60873
61333
  console.log(...args);
60874
61334
  };
61335
+ var EMPTY_LINE_IDS = [];
61336
+ var EMPTY_WORKSPACES = [];
60875
61337
  var LoadingPageCmp = LoadingPage_default;
61338
+ var LoadingOverlayCmp = LoadingOverlay_default;
60876
61339
  function HomeView({
60877
61340
  defaultLineId,
60878
61341
  factoryViewId,
@@ -61020,6 +61483,7 @@ function HomeView({
61020
61483
  return createNotificationService(supabaseClient);
61021
61484
  }, [supabaseClient]);
61022
61485
  const [bottleneckNotification, setBottleneckNotification] = React143.useState(null);
61486
+ const showBottleneckNotificationRef = React143.useRef(null);
61023
61487
  const [bottleneckModalOpen, setBottleneckModalOpen] = React143.useState(false);
61024
61488
  const [bottleneckModalData, setBottleneckModalData] = React143.useState(null);
61025
61489
  const [diagnosisModalOpen, setDiagnosisModalOpen] = React143.useState(false);
@@ -61030,6 +61494,11 @@ function HomeView({
61030
61494
  dashboardConfig?.shiftConfig
61031
61495
  );
61032
61496
  const shouldEnableMetricsFetch = authStatus === "ready";
61497
+ const liveMonitorMode = React143.useMemo(() => getLiveMonitorBootstrapMode(), []);
61498
+ const isLegacyMonitorMode = liveMonitorMode === "legacy";
61499
+ const isShadowMonitorMode = liveMonitorMode === "shadow";
61500
+ const isBootstrapMonitorMode = liveMonitorMode === "bootstrap";
61501
+ const lastShadowMismatchSignatureRef = React143.useRef(null);
61033
61502
  const handleLineMetricsUpdate = React143.useCallback(() => {
61034
61503
  if (trendRefreshTimerRef.current) {
61035
61504
  window.clearTimeout(trendRefreshTimerRef.current);
@@ -61045,45 +61514,24 @@ function HomeView({
61045
61514
  }
61046
61515
  }, []);
61047
61516
  const {
61048
- workspaceMetrics,
61049
- lineMetrics,
61050
- efficiencyLegend,
61051
- metadata: metricsMetadata,
61052
- isLoading: metricsLoading,
61053
- error: metricsError,
61054
- refetch: refetchMetrics
61517
+ workspaceMetrics: legacyWorkspaceMetrics,
61518
+ lineMetrics: legacyLineMetrics,
61519
+ efficiencyLegend: legacyEfficiencyLegend,
61520
+ metadata: legacyMetricsMetadata,
61521
+ isLoading: legacyMetricsLoading,
61522
+ isCurrentScopeResolved: legacyIsCurrentScopeResolved,
61523
+ error: legacyMetricsError,
61524
+ refetch: refetchLegacyMetrics
61055
61525
  } = useDashboardMetrics({
61056
61526
  lineId: metricsScopeLineId,
61057
61527
  lineIds: selectedLineIds,
61058
61528
  onLineMetricsUpdate: handleLineMetricsUpdate,
61059
61529
  userAccessibleLineIds: visibleLineIds,
61060
- // Pass user's accessible lines for supervisor filtering
61061
- enabled: shouldEnableMetricsFetch
61530
+ enabled: shouldEnableMetricsFetch && !isBootstrapMonitorMode
61062
61531
  });
61063
- const metricsDisplayNames = React143.useMemo(() => {
61064
- const nextDisplayNames = {};
61065
- workspaceMetrics.forEach((workspace) => {
61066
- if (!workspace.displayName) {
61067
- return;
61068
- }
61069
- nextDisplayNames[`${workspace.line_id}_${workspace.workspace_name}`] = workspace.displayName;
61070
- });
61071
- return nextDisplayNames;
61072
- }, [workspaceMetrics]);
61073
- React143.useEffect(() => {
61074
- workspaceMetrics.forEach((workspace) => {
61075
- if (!workspace.displayName) {
61076
- return;
61077
- }
61078
- upsertWorkspaceDisplayNameInCache({
61079
- lineId: workspace.line_id,
61080
- workspaceId: workspace.workspace_name,
61081
- displayName: workspace.displayName
61082
- });
61083
- });
61084
- }, [workspaceMetrics]);
61085
- const trendGroups = React143.useMemo(() => {
61086
- const lineMetricsRows = lineMetrics || [];
61532
+ const legacyTrendGroups = React143.useMemo(() => {
61533
+ if (isBootstrapMonitorMode) return null;
61534
+ const lineMetricsRows = legacyLineMetrics || [];
61087
61535
  if (!lineMetricsRows.length) return null;
61088
61536
  if (selectedLineIds.length > 1) {
61089
61537
  const rowsForLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
@@ -61109,7 +61557,7 @@ function HomeView({
61109
61557
  shiftId: group.shiftId
61110
61558
  }));
61111
61559
  }
61112
- const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
61560
+ const row = lineMetricsRows.find((candidate) => candidate?.line_id === primarySelectedLineId);
61113
61561
  if (!row?.date || row?.shift_id === void 0 || row?.shift_id === null) {
61114
61562
  return null;
61115
61563
  }
@@ -61118,69 +61566,115 @@ function HomeView({
61118
61566
  date: row.date,
61119
61567
  shiftId: row.shift_id
61120
61568
  }];
61121
- }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61122
- const trendOptions = React143.useMemo(() => {
61123
- if (!trendGroups || !userCompanyId) return null;
61569
+ }, [isBootstrapMonitorMode, legacyLineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61570
+ const legacyTrendOptions = React143.useMemo(() => {
61571
+ if (isBootstrapMonitorMode || !legacyTrendGroups || !userCompanyId) return null;
61124
61572
  return {
61125
- groups: trendGroups,
61573
+ groups: legacyTrendGroups,
61126
61574
  companyId: userCompanyId,
61127
61575
  refreshKey: trendRefreshKey,
61128
61576
  forceRefresh: trendRefreshKey > 0
61129
61577
  };
61130
- }, [trendGroups, userCompanyId, trendRefreshKey]);
61131
- const { trend: kpiTrend } = useKpiTrends(trendOptions);
61132
- const hasFlowBuffers = Boolean(metricsMetadata?.hasFlowBuffers);
61578
+ }, [isBootstrapMonitorMode, legacyTrendGroups, userCompanyId, trendRefreshKey]);
61579
+ const { trend: legacyKpiTrend } = useKpiTrends(legacyTrendOptions);
61580
+ const bootstrapMonitor = useLiveMonitorBootstrap({
61581
+ lineIds: selectedLineIds,
61582
+ companyId: userCompanyId,
61583
+ enabled: shouldEnableMetricsFetch && !isLegacyMonitorMode
61584
+ });
61585
+ const currentWorkspaceMetrics = isBootstrapMonitorMode ? bootstrapMonitor.workspaceMetrics : legacyWorkspaceMetrics;
61586
+ const currentLineMetrics = isBootstrapMonitorMode ? bootstrapMonitor.lineMetrics : legacyLineMetrics;
61587
+ const currentEfficiencyLegend = isBootstrapMonitorMode ? bootstrapMonitor.efficiencyLegend : legacyEfficiencyLegend;
61588
+ const currentMetricsMetadata = isBootstrapMonitorMode ? bootstrapMonitor.metadata : legacyMetricsMetadata;
61589
+ const currentMetricsLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyMetricsLoading;
61590
+ const currentIsCurrentScopeResolved = isBootstrapMonitorMode ? bootstrapMonitor.isCurrentScopeResolved : legacyIsCurrentScopeResolved;
61591
+ const currentMetricsError = isBootstrapMonitorMode ? bootstrapMonitor.error : legacyMetricsError;
61592
+ const currentRefetchMetrics = isBootstrapMonitorMode ? bootstrapMonitor.refetch : refetchLegacyMetrics;
61593
+ const metricsDisplayNames = React143.useMemo(() => {
61594
+ const nextDisplayNames = {};
61595
+ currentWorkspaceMetrics.forEach((workspace) => {
61596
+ if (!workspace.displayName) {
61597
+ return;
61598
+ }
61599
+ nextDisplayNames[`${workspace.line_id}_${workspace.workspace_name}`] = workspace.displayName;
61600
+ });
61601
+ return nextDisplayNames;
61602
+ }, [currentWorkspaceMetrics]);
61603
+ React143.useEffect(() => {
61604
+ currentWorkspaceMetrics.forEach((workspace) => {
61605
+ if (!workspace.displayName) {
61606
+ return;
61607
+ }
61608
+ upsertWorkspaceDisplayNameInCache({
61609
+ lineId: workspace.line_id,
61610
+ workspaceId: workspace.workspace_name,
61611
+ displayName: workspace.displayName
61612
+ });
61613
+ });
61614
+ }, [currentWorkspaceMetrics]);
61615
+ const hasFlowBuffers = Boolean(currentMetricsMetadata?.hasFlowBuffers);
61133
61616
  React143.useEffect(() => {
61134
- const sample = workspaceMetrics.slice(0, 3).map((workspace) => ({
61617
+ const sample = currentWorkspaceMetrics.slice(0, 3).map((workspace) => ({
61135
61618
  id: workspace.workspace_uuid || workspace.workspace_name,
61136
61619
  efficiency: workspace.efficiency,
61137
61620
  trend: workspace.trend,
61138
61621
  lineId: workspace.line_id
61139
61622
  }));
61140
61623
  logDebug3("[HomeView] workspaceMetrics update:", {
61141
- count: workspaceMetrics.length,
61142
- isLoading: metricsLoading,
61143
- error: metricsError?.message,
61624
+ count: currentWorkspaceMetrics.length,
61625
+ isLoading: currentMetricsLoading,
61626
+ error: currentMetricsError?.message,
61144
61627
  sample
61145
61628
  });
61146
- }, [workspaceMetrics, metricsLoading, metricsError]);
61147
- const kpis = React143.useMemo(() => {
61148
- const lineMetricsRows = lineMetrics || [];
61629
+ }, [currentWorkspaceMetrics, currentMetricsLoading, currentMetricsError]);
61630
+ const buildDisplayKpis = React143.useCallback((sourceLineMetrics, sourceIsLoading, sourceKpiTrend) => {
61631
+ const lineMetricsRows = sourceLineMetrics || [];
61632
+ let baseKpis = null;
61149
61633
  if (selectedLineIds.length > 1) {
61150
- const rowsForSelectedLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
61151
- if (metricsLoading && rowsForSelectedLines.length === 0) return null;
61152
- return aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
61153
- }
61154
- const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
61155
- if (!row) {
61156
- if (metricsLoading) return null;
61157
- return buildKPIsFromLineMetricsRow(null);
61158
- }
61159
- return buildKPIsFromLineMetricsRow(row);
61160
- }, [lineMetrics, metricsLoading, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61161
- const kpisWithTrend = React143.useMemo(() => {
61162
- if (!kpis) return null;
61163
- if (!kpiTrend) return kpis;
61634
+ const rowsForSelectedLines = lineMetricsRows.filter((row) => selectedLineIdSet.has(row?.line_id));
61635
+ if (sourceIsLoading && rowsForSelectedLines.length === 0) return null;
61636
+ baseKpis = aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
61637
+ } else {
61638
+ const row = lineMetricsRows.find((candidate) => candidate?.line_id === primarySelectedLineId);
61639
+ if (!row) {
61640
+ if (sourceIsLoading) return null;
61641
+ baseKpis = buildKPIsFromLineMetricsRow(null);
61642
+ } else {
61643
+ baseKpis = buildKPIsFromLineMetricsRow(row);
61644
+ }
61645
+ }
61646
+ if (!baseKpis || !sourceKpiTrend) {
61647
+ return baseKpis;
61648
+ }
61164
61649
  return {
61165
- ...kpis,
61650
+ ...baseKpis,
61166
61651
  efficiency: {
61167
- ...kpis.efficiency,
61168
- change: kpiTrend.efficiency?.delta_pp ?? kpis.efficiency.change
61652
+ ...baseKpis.efficiency,
61653
+ change: sourceKpiTrend.efficiency?.delta_pp ?? baseKpis.efficiency.change
61169
61654
  },
61170
61655
  outputProgress: {
61171
- ...kpis.outputProgress,
61172
- change: kpiTrend.outputProgress?.delta_pp ?? kpis.outputProgress.change
61656
+ ...baseKpis.outputProgress,
61657
+ change: sourceKpiTrend.outputProgress?.delta_pp ?? baseKpis.outputProgress.change
61173
61658
  },
61174
61659
  underperformingWorkers: {
61175
- ...kpis.underperformingWorkers,
61176
- change: kpiTrend.underperformingWorkers?.delta_count ?? kpis.underperformingWorkers.change
61660
+ ...baseKpis.underperformingWorkers,
61661
+ change: sourceKpiTrend.underperformingWorkers?.delta_count ?? baseKpis.underperformingWorkers.change
61177
61662
  },
61178
61663
  avgCycleTime: {
61179
- ...kpis.avgCycleTime,
61180
- change: kpiTrend.avgCycleTime?.delta_seconds ?? kpis.avgCycleTime.change
61664
+ ...baseKpis.avgCycleTime,
61665
+ change: sourceKpiTrend.avgCycleTime?.delta_seconds ?? baseKpis.avgCycleTime.change
61181
61666
  }
61182
61667
  };
61183
- }, [kpis, kpiTrend]);
61668
+ }, [primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61669
+ const legacyKpisWithTrend = React143.useMemo(
61670
+ () => buildDisplayKpis(legacyLineMetrics, legacyMetricsLoading, legacyKpiTrend),
61671
+ [buildDisplayKpis, legacyLineMetrics, legacyMetricsLoading, legacyKpiTrend]
61672
+ );
61673
+ const bootstrapKpisWithTrend = React143.useMemo(
61674
+ () => buildDisplayKpis(bootstrapMonitor.lineMetrics, bootstrapMonitor.isLoading, bootstrapMonitor.kpiTrend),
61675
+ [buildDisplayKpis, bootstrapMonitor.lineMetrics, bootstrapMonitor.isLoading, bootstrapMonitor.kpiTrend]
61676
+ );
61677
+ const currentKpisWithTrend = isBootstrapMonitorMode ? bootstrapKpisWithTrend : legacyKpisWithTrend;
61184
61678
  const selectedLineMeta = React143.useMemo(
61185
61679
  () => selectedLineIds.length === 1 ? dbLines.find((line) => line.id === primarySelectedLineId) : void 0,
61186
61680
  [dbLines, primarySelectedLineId, selectedLineIds.length]
@@ -61189,40 +61683,121 @@ function HomeView({
61189
61683
  const isUptimeMode = selectedMonitoringMode === "uptime";
61190
61684
  const averageIdleTimeSeconds = React143.useMemo(() => {
61191
61685
  if (!isUptimeMode) return null;
61192
- const targetWorkspaces = workspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
61686
+ const targetWorkspaces = currentWorkspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
61193
61687
  const idleValues = targetWorkspaces.map((ws) => ws.idle_time).filter((value) => Number.isFinite(value));
61194
61688
  if (idleValues.length === 0) return 0;
61195
61689
  const totalIdle = idleValues.reduce((sum, value) => sum + value, 0);
61196
61690
  return totalIdle / idleValues.length;
61197
- }, [isUptimeMode, primarySelectedLineId, workspaceMetrics]);
61691
+ }, [isUptimeMode, primarySelectedLineId, currentWorkspaceMetrics]);
61198
61692
  const {
61199
- activeBreaks: allActiveBreaks,
61200
- isLoading: breaksLoading,
61201
- error: breaksError
61202
- } = useActiveBreaks(visibleLineIds);
61203
- const activeBreaks = React143.useMemo(() => {
61693
+ activeBreaks: legacyAllActiveBreaks,
61694
+ isLoading: legacyBreaksLoading,
61695
+ error: legacyBreaksError
61696
+ } = useActiveBreaks(isBootstrapMonitorMode ? EMPTY_LINE_IDS : visibleLineIds);
61697
+ const legacyActiveBreaks = React143.useMemo(() => {
61204
61698
  if (isAllLinesSelection(selectedLineIds)) {
61205
- return allActiveBreaks;
61699
+ return legacyAllActiveBreaks;
61206
61700
  }
61207
- return allActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
61208
- }, [allActiveBreaks, selectedLineIdSet, selectedLineIds]);
61701
+ return legacyAllActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
61702
+ }, [legacyAllActiveBreaks, selectedLineIdSet, selectedLineIds]);
61703
+ const currentActiveBreaks = isBootstrapMonitorMode ? bootstrapMonitor.activeBreaks : legacyActiveBreaks;
61704
+ const currentBreaksLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyBreaksLoading;
61705
+ const currentBreaksError = isBootstrapMonitorMode ? bootstrapMonitor.error?.message || null : legacyBreaksError;
61209
61706
  const activeBreakLineIds = React143.useMemo(
61210
- () => new Set(activeBreaks.map((breakItem) => breakItem.lineId)),
61211
- [activeBreaks]
61707
+ () => new Set(currentActiveBreaks.map((breakItem) => breakItem.lineId)),
61708
+ [currentActiveBreaks]
61212
61709
  );
61213
61710
  const workspaceMetricsWithBreakState = React143.useMemo(
61214
- () => workspaceMetrics.map((workspace) => ({
61711
+ () => currentWorkspaceMetrics.map((workspace) => ({
61215
61712
  ...workspace,
61216
61713
  scheduled_break_active: activeBreakLineIds.has(workspace.line_id)
61217
61714
  })),
61218
- [workspaceMetrics, activeBreakLineIds]
61715
+ [currentWorkspaceMetrics, activeBreakLineIds]
61219
61716
  );
61220
61717
  const [breakNotificationsDismissed, setBreakNotificationsDismissed] = React143.useState(false);
61221
61718
  React143.useEffect(() => {
61222
- if (activeBreaks.length > 0) {
61719
+ if (currentActiveBreaks.length > 0) {
61223
61720
  setBreakNotificationsDismissed(false);
61224
61721
  }
61225
- }, [activeBreaks.length]);
61722
+ }, [currentActiveBreaks.length]);
61723
+ const {
61724
+ streamsByWorkspaceId: legacyVideoStreamsByWorkspaceId,
61725
+ isLoading: legacyVideoStreamsLoading
61726
+ } = useWorkspaceVideoStreams(isBootstrapMonitorMode ? EMPTY_WORKSPACES : legacyWorkspaceMetrics, {
61727
+ lineIds: isBootstrapMonitorMode ? EMPTY_LINE_IDS : selectedLineIds
61728
+ });
61729
+ const currentVideoStreamsByWorkspaceId = isBootstrapMonitorMode ? bootstrapMonitor.videoStreamsByWorkspaceId : legacyVideoStreamsByWorkspaceId;
61730
+ const currentVideoStreamsLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyVideoStreamsLoading;
61731
+ React143.useEffect(() => {
61732
+ if (!isShadowMonitorMode) {
61733
+ lastShadowMismatchSignatureRef.current = null;
61734
+ return;
61735
+ }
61736
+ if (!shouldEnableMetricsFetch || legacyMetricsLoading || !legacyIsCurrentScopeResolved || bootstrapMonitor.isLoading || !bootstrapMonitor.isCurrentScopeResolved) {
61737
+ return;
61738
+ }
61739
+ const legacySnapshot = normalizeMonitorShadowSnapshot({
61740
+ selectedLineIds,
61741
+ workspaces: workspaceMetricsWithBreakState,
61742
+ kpis: legacyKpisWithTrend,
61743
+ kpiTrend: legacyKpiTrend,
61744
+ activeBreaks: legacyActiveBreaks,
61745
+ videoStreamsByWorkspaceId: legacyVideoStreamsByWorkspaceId,
61746
+ efficiencyLegend: legacyEfficiencyLegend
61747
+ });
61748
+ const bootstrapSnapshot = normalizeMonitorShadowSnapshot({
61749
+ selectedLineIds,
61750
+ resolvedScope: bootstrapMonitor.resolvedScope,
61751
+ lines: bootstrapMonitor.lines,
61752
+ workspaces: bootstrapMonitor.workspaceMetrics.map((workspace) => ({
61753
+ ...workspace,
61754
+ scheduled_break_active: bootstrapMonitor.activeBreaks.some((activeBreak) => activeBreak.lineId === workspace.line_id)
61755
+ })),
61756
+ kpis: bootstrapKpisWithTrend,
61757
+ kpiTrend: bootstrapMonitor.kpiTrend,
61758
+ activeBreaks: bootstrapMonitor.activeBreaks,
61759
+ videoStreamsByWorkspaceId: bootstrapMonitor.videoStreamsByWorkspaceId,
61760
+ efficiencyLegend: bootstrapMonitor.efficiencyLegend
61761
+ });
61762
+ const mismatches = diffMonitorShadowSnapshots(legacySnapshot, bootstrapSnapshot);
61763
+ const signature = JSON.stringify(mismatches);
61764
+ if (mismatches.length === 0) {
61765
+ lastShadowMismatchSignatureRef.current = null;
61766
+ return;
61767
+ }
61768
+ if (lastShadowMismatchSignatureRef.current === signature) {
61769
+ return;
61770
+ }
61771
+ lastShadowMismatchSignatureRef.current = signature;
61772
+ console.error("[HomeView][monitor-shadow-mismatch]", {
61773
+ selectedLineIds,
61774
+ mismatches,
61775
+ legacySnapshot,
61776
+ bootstrapSnapshot
61777
+ });
61778
+ }, [
61779
+ bootstrapKpisWithTrend,
61780
+ bootstrapMonitor.activeBreaks,
61781
+ bootstrapMonitor.efficiencyLegend,
61782
+ bootstrapMonitor.isCurrentScopeResolved,
61783
+ bootstrapMonitor.isLoading,
61784
+ bootstrapMonitor.kpiTrend,
61785
+ bootstrapMonitor.lines,
61786
+ bootstrapMonitor.resolvedScope,
61787
+ bootstrapMonitor.videoStreamsByWorkspaceId,
61788
+ bootstrapMonitor.workspaceMetrics,
61789
+ isShadowMonitorMode,
61790
+ legacyActiveBreaks,
61791
+ legacyEfficiencyLegend,
61792
+ legacyIsCurrentScopeResolved,
61793
+ legacyKpiTrend,
61794
+ legacyKpisWithTrend,
61795
+ legacyMetricsLoading,
61796
+ legacyVideoStreamsByWorkspaceId,
61797
+ selectedLineIds,
61798
+ shouldEnableMetricsFetch,
61799
+ workspaceMetricsWithBreakState
61800
+ ]);
61226
61801
  const showBottleneckNotification = React143.useCallback(async (bottleneck) => {
61227
61802
  try {
61228
61803
  logDebug3("\u{1F514} [Notification] Raw bottleneck data:", bottleneck);
@@ -61400,6 +61975,9 @@ function HomeView({
61400
61975
  setBottleneckNotification(errorNotification);
61401
61976
  }
61402
61977
  }, [notificationService, timezone, dashboardConfig, lineShiftConfigs]);
61978
+ React143.useEffect(() => {
61979
+ showBottleneckNotificationRef.current = showBottleneckNotification;
61980
+ }, [showBottleneckNotification]);
61403
61981
  React143.useEffect(() => {
61404
61982
  const ticketsEnabled = dashboardConfig?.ticketsConfig?.enabled ?? true;
61405
61983
  if (!ticketsEnabled) {
@@ -61469,7 +62047,7 @@ function HomeView({
61469
62047
  });
61470
62048
  if (latestTicket.event_type === "bottleneck") {
61471
62049
  logDebug3("\u{1F514} [Polling] Showing bottleneck notification for:", latestTicket.log_number);
61472
- showBottleneckNotification(latestTicket);
62050
+ await showBottleneckNotificationRef.current?.(latestTicket);
61473
62051
  } else {
61474
62052
  logDebug3("\u26A0\uFE0F [Polling] Non-bottleneck ticket found but no handler configured:", latestTicket.event_type);
61475
62053
  }
@@ -61489,29 +62067,30 @@ function HomeView({
61489
62067
  clearInterval(pollInterval);
61490
62068
  clearTimeout(initialPollTimer);
61491
62069
  };
61492
- }, [notificationService, userCompanyId, user, showBottleneckNotification, dashboardConfig?.ticketsConfig?.enabled]);
62070
+ }, [
62071
+ dashboardConfig?.ticketsConfig?.enabled,
62072
+ notificationService,
62073
+ user?.email,
62074
+ user?.role_level,
62075
+ userCompanyId
62076
+ ]);
61493
62077
  const handleWorkspaceHover = React143.useCallback((workspaceId) => {
61494
62078
  }, []);
61495
62079
  const handleWorkspaceHoverEnd = React143.useCallback((workspaceId) => {
61496
62080
  }, []);
61497
- const {
61498
- streamsByWorkspaceId: videoStreamsByWorkspaceId,
61499
- isLoading: videoStreamsLoading
61500
- } = useWorkspaceVideoStreams(workspaceMetrics);
61501
- const memoizedKPIs = React143.useMemo(() => kpisWithTrend, [
62081
+ const memoizedKPIs = React143.useMemo(() => currentKpisWithTrend, [
61502
62082
  // Only update reference when values change by at least 1%
61503
- kpisWithTrend?.efficiency?.value ? Math.round(kpisWithTrend.efficiency.value) : null,
61504
- kpisWithTrend?.efficiency?.change,
61505
- kpisWithTrend?.underperformingWorkers?.current,
61506
- kpisWithTrend?.underperformingWorkers?.total,
61507
- kpisWithTrend?.underperformingWorkers?.change,
61508
- kpisWithTrend?.outputProgress?.current,
61509
- kpisWithTrend?.outputProgress?.target,
61510
- kpisWithTrend?.outputProgress?.change,
61511
- kpisWithTrend?.avgCycleTime?.value ? Math.round(kpisWithTrend.avgCycleTime.value * 10) / 10 : null,
61512
- // Round to 1 decimal
61513
- kpisWithTrend?.avgCycleTime?.change,
61514
- kpisWithTrend?.qualityCompliance?.value ? Math.round(kpisWithTrend.qualityCompliance.value) : null,
62083
+ currentKpisWithTrend?.efficiency?.value ? Math.round(currentKpisWithTrend.efficiency.value) : null,
62084
+ currentKpisWithTrend?.efficiency?.change,
62085
+ currentKpisWithTrend?.underperformingWorkers?.current,
62086
+ currentKpisWithTrend?.underperformingWorkers?.total,
62087
+ currentKpisWithTrend?.underperformingWorkers?.change,
62088
+ currentKpisWithTrend?.outputProgress?.current,
62089
+ currentKpisWithTrend?.outputProgress?.target,
62090
+ currentKpisWithTrend?.outputProgress?.change,
62091
+ currentKpisWithTrend?.avgCycleTime?.value ? Math.round(currentKpisWithTrend.avgCycleTime.value * 10) / 10 : null,
62092
+ currentKpisWithTrend?.avgCycleTime?.change,
62093
+ currentKpisWithTrend?.qualityCompliance?.value ? Math.round(currentKpisWithTrend.qualityCompliance.value) : null,
61515
62094
  selectedLineIdsKey
61516
62095
  ]);
61517
62096
  React143.useEffect(() => {
@@ -61522,13 +62101,13 @@ function HomeView({
61522
62101
  dashboard_surface: "monitor"
61523
62102
  });
61524
62103
  }, []);
61525
- const metricsErrorMessage = metricsError?.message || null;
62104
+ const metricsErrorMessage = currentMetricsError?.message || null;
61526
62105
  const handleRetryDashboardData = React143.useCallback(async () => {
61527
62106
  if (isRecoveringSession) {
61528
62107
  await retrySessionHydration();
61529
62108
  }
61530
- refetchMetrics();
61531
- }, [isRecoveringSession, refetchMetrics, retrySessionHydration]);
62109
+ await currentRefetchMetrics();
62110
+ }, [currentRefetchMetrics, isRecoveringSession, retrySessionHydration]);
61532
62111
  const getTrackedLineScope = React143.useCallback((lineIdsForScope) => {
61533
62112
  if (isAllLinesSelection(lineIdsForScope)) {
61534
62113
  return factoryViewId;
@@ -61581,12 +62160,15 @@ function HomeView({
61581
62160
  updateSelectedLineIds(Array.from(currentSelection));
61582
62161
  }, [selectedLineIds, updateSelectedLineIds]);
61583
62162
  React143.useEffect(() => {
61584
- if (!metricsLoading && isChangingFilter) {
62163
+ if (!isChangingFilter) {
62164
+ return;
62165
+ }
62166
+ if (!currentMetricsLoading && currentIsCurrentScopeResolved || currentMetricsError) {
61585
62167
  setIsChangingFilter(false);
61586
62168
  }
61587
- }, [metricsLoading, isChangingFilter]);
62169
+ }, [currentIsCurrentScopeResolved, currentMetricsError, currentMetricsLoading, isChangingFilter]);
61588
62170
  React143.useEffect(() => {
61589
- if (shouldEnableMetricsFetch && !metricsLoading && !metricsError && !hasInitialDataLoaded) {
62171
+ if (shouldEnableMetricsFetch && !currentMetricsLoading && !currentMetricsError && !hasInitialDataLoaded) {
61590
62172
  setHasInitialDataLoaded(true);
61591
62173
  trackCoreEvent("monitor page loaded", {
61592
62174
  default_line_id: defaultLineId,
@@ -61595,7 +62177,7 @@ function HomeView({
61595
62177
  dashboard_surface: "monitor"
61596
62178
  });
61597
62179
  }
61598
- }, [shouldEnableMetricsFetch, metricsLoading, metricsError, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
62180
+ }, [shouldEnableMetricsFetch, currentMetricsLoading, currentMetricsError, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
61599
62181
  const lineTitle = React143.useMemo(() => {
61600
62182
  return factoryName;
61601
62183
  }, [factoryName]);
@@ -61746,15 +62328,15 @@ function HomeView({
61746
62328
  return showLoading;
61747
62329
  };
61748
62330
  const isAuthBootstrapping = authStatus === "loading";
61749
- const isInitialLoading = !isHydrated || !hasInitialDataLoaded && (isAuthBootstrapping || shouldEnableMetricsFetch && metricsLoading);
61750
- const isDataLoading = metricsLoading;
62331
+ const isInitialLoading = !isHydrated || !hasInitialDataLoaded && (isAuthBootstrapping || shouldEnableMetricsFetch && currentMetricsLoading);
62332
+ const isDataLoading = currentMetricsLoading;
61751
62333
  const hasKpiDataReady = React143.useMemo(() => {
61752
- const lineMetricsRows = lineMetrics || [];
62334
+ const lineMetricsRows = currentLineMetrics || [];
61753
62335
  if (selectedLineIds.length > 1) {
61754
62336
  return lineMetricsRows.some((row) => selectedLineIdSet.has(row?.line_id));
61755
62337
  }
61756
62338
  return lineMetricsRows.some((row) => row?.line_id === primarySelectedLineId);
61757
- }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
62339
+ }, [currentLineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61758
62340
  const isKpiLoading = !hasKpiDataReady;
61759
62341
  const shouldShowReconnectScreen = !hasInitialDataLoaded && (isRecoveringSession || !shouldEnableMetricsFetch && authStatus !== "failed" || !!metricsErrorMessage && authStatus !== "failed");
61760
62342
  const shouldShowFatalLoadFailure = !hasInitialDataLoaded && !!metricsErrorMessage && authStatus === "failed";
@@ -61782,6 +62364,7 @@ function HomeView({
61782
62364
  return () => clearTimeout(timer);
61783
62365
  }, [isDataLoading]);
61784
62366
  const shouldShowDataLoading = showDataLoading || isDataLoading;
62367
+ const shouldShowFilterChangeLoader = isChangingFilter && !currentIsCurrentScopeResolved;
61785
62368
  const shouldShowKpiLoading = useSmoothLoading(isKpiLoading, 400);
61786
62369
  const displayKpis = shouldShowKpiLoading ? null : memoizedKPIs;
61787
62370
  const kpiSectionControl = React143.useMemo(() => /* @__PURE__ */ jsxRuntime.jsx(
@@ -61866,7 +62449,7 @@ function HomeView({
61866
62449
  }
61867
62450
  )
61868
62451
  ] }) }) : null,
61869
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
62452
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
61870
62453
  motion.div,
61871
62454
  {
61872
62455
  initial: { opacity: 0, scale: 0.98 },
@@ -61878,10 +62461,10 @@ function HomeView({
61878
62461
  lineNames: mergedLineNames,
61879
62462
  lineOrder: selectedLineIds,
61880
62463
  factoryView: factoryViewId,
61881
- legend: efficiencyLegend,
62464
+ legend: currentEfficiencyLegend,
61882
62465
  videoSources,
61883
- videoStreamsByWorkspaceId,
61884
- videoStreamsLoading,
62466
+ videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
62467
+ videoStreamsLoading: currentVideoStreamsLoading,
61885
62468
  displayNames: metricsDisplayNames,
61886
62469
  hasFlowBuffers,
61887
62470
  className: "h-full",
@@ -61889,8 +62472,7 @@ function HomeView({
61889
62472
  onWorkspaceHover: handleWorkspaceHover,
61890
62473
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
61891
62474
  })
61892
- },
61893
- selectedLineIdsKey
62475
+ }
61894
62476
  ) : !shouldShowDataLoading && hasInitialDataLoaded ? /* @__PURE__ */ jsxRuntime.jsx(
61895
62477
  motion.div,
61896
62478
  {
@@ -61912,10 +62494,10 @@ function HomeView({
61912
62494
  lineNames: mergedLineNames,
61913
62495
  lineOrder: selectedLineIds,
61914
62496
  factoryView: factoryViewId,
61915
- legend: efficiencyLegend,
62497
+ legend: currentEfficiencyLegend,
61916
62498
  videoSources,
61917
- videoStreamsByWorkspaceId,
61918
- videoStreamsLoading,
62499
+ videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
62500
+ videoStreamsLoading: currentVideoStreamsLoading,
61919
62501
  displayNames: metricsDisplayNames,
61920
62502
  hasFlowBuffers,
61921
62503
  className: "h-full",
@@ -61923,16 +62505,15 @@ function HomeView({
61923
62505
  onWorkspaceHover: handleWorkspaceHover,
61924
62506
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
61925
62507
  })
61926
- },
61927
- selectedLineIdsKey
62508
+ }
61928
62509
  ) }) })
61929
62510
  ] }),
61930
62511
  /* @__PURE__ */ jsxRuntime.jsx(
61931
62512
  BreakNotificationPopup,
61932
62513
  {
61933
- activeBreaks,
62514
+ activeBreaks: currentActiveBreaks,
61934
62515
  lineNames: mergedLineNames,
61935
- isVisible: !breaksLoading && !breaksError && !breakNotificationsDismissed,
62516
+ isVisible: !currentBreaksLoading && !currentBreaksError && !breakNotificationsDismissed,
61936
62517
  onDismiss: () => setBreakNotificationsDismissed(true)
61937
62518
  }
61938
62519
  ),
@@ -61944,6 +62525,15 @@ function HomeView({
61944
62525
  onDismiss: () => setBottleneckNotification(null)
61945
62526
  }
61946
62527
  ),
62528
+ /* @__PURE__ */ jsxRuntime.jsx(
62529
+ LoadingOverlayCmp,
62530
+ {
62531
+ isVisible: shouldShowFilterChangeLoader,
62532
+ message: "Loading Dashboard...",
62533
+ className: "bg-slate-50/30 backdrop-blur-[3px]",
62534
+ contentVariant: "plain"
62535
+ }
62536
+ ),
61947
62537
  diagnosisModalOpen && bottleneckModalData?.clipId && /* @__PURE__ */ jsxRuntime.jsx(
61948
62538
  DiagnosisVideoModal,
61949
62539
  {
@@ -62194,7 +62784,7 @@ var buildLineInfoSnapshot = (lineDetails, metrics2) => {
62194
62784
  }
62195
62785
  };
62196
62786
  };
62197
- var transformWorkspaceMetrics = (workspaceData, lineId, companyId, queryDate, queryShiftId) => (workspaceData || []).map((item) => ({
62787
+ var transformWorkspaceMetrics2 = (workspaceData, lineId, companyId, queryDate, queryShiftId) => (workspaceData || []).map((item) => ({
62198
62788
  company_id: item.company_id || companyId,
62199
62789
  line_id: item.line_id || lineId,
62200
62790
  shift_id: item.shift_id ?? queryShiftId,
@@ -62342,7 +62932,7 @@ var useLineDetailPageData = ({
62342
62932
  setDetailResponse(nextDetail);
62343
62933
  const metrics3 = transformLineMetrics(lineId, nextDetail, queryDate, queryShiftId);
62344
62934
  const lineDetails2 = transformLineDetails(lineId, nextDetail);
62345
- const workspaces2 = transformWorkspaceMetrics(
62935
+ const workspaces2 = transformWorkspaceMetrics2(
62346
62936
  nextDetail.workspace_metrics || [],
62347
62937
  lineId,
62348
62938
  companyId,
@@ -62432,7 +63022,7 @@ var useLineDetailPageData = ({
62432
63022
  [detailResponse, lineId]
62433
63023
  );
62434
63024
  const workspaces = React143.useMemo(
62435
- () => detailResponse && companyId ? transformWorkspaceMetrics(detailResponse.workspace_metrics || [], lineId, companyId, queryDate, queryShiftId) : [],
63025
+ () => detailResponse && companyId ? transformWorkspaceMetrics2(detailResponse.workspace_metrics || [], lineId, companyId, queryDate, queryShiftId) : [],
62436
63026
  [detailResponse, companyId, lineId, queryDate, queryShiftId]
62437
63027
  );
62438
63028
  const lineInfo = React143.useMemo(() => buildLineInfoSnapshot(lineDetails, metrics2), [lineDetails, metrics2]);
@@ -74054,7 +74644,8 @@ var TeamManagementView = ({
74054
74644
  const bootstrapData = await bootstrapResponse.json();
74055
74645
  setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
74056
74646
  id: factory.id,
74057
- factory_name: factory.factory_name
74647
+ factory_name: factory.factory_name,
74648
+ company_id: factory.company_id
74058
74649
  })));
74059
74650
  setAvailableLines(bootstrapData.lines || []);
74060
74651
  setUsers(bootstrapData.users || []);
@@ -79022,7 +79613,7 @@ var efficiencyLineConfig = [
79022
79613
  var bumpRenderCounter = (key) => {
79023
79614
  if (process.env.NODE_ENV === "test") ;
79024
79615
  };
79025
- var toNumber3 = (value) => {
79616
+ var toNumber4 = (value) => {
79026
79617
  if (typeof value === "number" && Number.isFinite(value)) return value;
79027
79618
  if (typeof value === "string" && value.trim().length > 0) {
79028
79619
  const parsed = Number(value);
@@ -79557,7 +80148,7 @@ var OverviewSummaryCards = React143__namespace.default.memo(({ store }) => {
79557
80148
  workspaceId: item.workspace_id || "",
79558
80149
  workspaceName: item.workspace_name?.trim() || item.workspace_id || "Unknown",
79559
80150
  lineName: item.line_name?.trim() || "Unknown Line",
79560
- avgIdleSeconds: toNumber3(item.avg_idle_seconds)
80151
+ avgIdleSeconds: toNumber4(item.avg_idle_seconds)
79561
80152
  })).slice(0, 5);
79562
80153
  }, [snapshot.data.summary.avg_idle_per_workstation?.top_contributors]);
79563
80154
  const showIdleContributorLineNames = React143__namespace.default.useMemo(() => {
@@ -79772,9 +80363,9 @@ var PoorestPerformersCard = React143__namespace.default.memo(({
79772
80363
  return {
79773
80364
  id: lineId,
79774
80365
  name: line.line_name?.trim() || "Unknown Line",
79775
- efficiency: roundOne(toNumber3(line.avg_efficiency) || 0),
79776
- previousEfficiency: toNumber3(line.previous_avg_efficiency),
79777
- delta: toNumber3(line.delta_pp),
80366
+ efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
80367
+ previousEfficiency: toNumber4(line.previous_avg_efficiency),
80368
+ delta: toNumber4(line.delta_pp),
79778
80369
  supervisor: supervisor?.displayName || "Unassigned",
79779
80370
  supervisorImage: supervisor?.profilePhotoUrl ?? null
79780
80371
  };
@@ -79883,14 +80474,14 @@ var IdleBreakdownCard = React143__namespace.default.memo(({
79883
80474
  paletteToken: item.palette_token?.trim(),
79884
80475
  iconToken: item.icon_token?.trim(),
79885
80476
  isKnown: item.is_known ?? void 0,
79886
- value: toNumber3(item.percentage) || 0,
79887
- totalDurationSeconds: toNumber3(item.total_duration_seconds),
79888
- efficiencyLossPercentage: toNumber3(item.efficiency_loss_percentage),
80477
+ value: toNumber4(item.percentage) || 0,
80478
+ totalDurationSeconds: toNumber4(item.total_duration_seconds),
80479
+ efficiencyLossPercentage: toNumber4(item.efficiency_loss_percentage),
79889
80480
  contributors: (item.contributors || []).map((contributor) => ({
79890
80481
  workspaceId: contributor.workspace_id || "",
79891
80482
  workspaceName: contributor.workspace_name?.trim() || "Unknown",
79892
- totalDurationSeconds: toNumber3(contributor.total_duration_seconds),
79893
- percentageWithinReason: toNumber3(contributor.percentage_within_reason)
80483
+ totalDurationSeconds: toNumber4(contributor.total_duration_seconds),
80484
+ percentageWithinReason: toNumber4(contributor.percentage_within_reason)
79894
80485
  }))
79895
80486
  })).filter((item) => item.value > 0);
79896
80487
  }, [idle.data]);
@@ -79959,7 +80550,7 @@ var EfficiencyTrendCard = React143__namespace.default.memo(({
79959
80550
  return `${hour12}:${minutes.toString().padStart(2, "0")} ${suffix}`;
79960
80551
  })(),
79961
80552
  efficiency: (() => {
79962
- const value = toNumber3(point.avg_efficiency);
80553
+ const value = toNumber4(point.avg_efficiency);
79963
80554
  return value === null ? void 0 : value;
79964
80555
  })()
79965
80556
  }));
@@ -79972,7 +80563,7 @@ var EfficiencyTrendCard = React143__namespace.default.memo(({
79972
80563
  name: dateFns.format(pointDate, "MMM d"),
79973
80564
  dayOfWeek: dateFns.format(pointDate, "EEEE"),
79974
80565
  efficiency: (() => {
79975
- const value = toNumber3(point.avg_efficiency);
80566
+ const value = toNumber4(point.avg_efficiency);
79976
80567
  return value === null ? void 0 : value;
79977
80568
  })()
79978
80569
  }]];
@@ -79996,7 +80587,7 @@ var EfficiencyTrendCard = React143__namespace.default.memo(({
79996
80587
  name: pointDate ? dateFns.format(pointDate, "MMM d") : "",
79997
80588
  dayOfWeek: pointDate ? dateFns.format(pointDate, "EEEE") : "",
79998
80589
  efficiency: (() => {
79999
- const value = toNumber3(point.avg_efficiency);
80590
+ const value = toNumber4(point.avg_efficiency);
80000
80591
  return value === null ? void 0 : value;
80001
80592
  })()
80002
80593
  };
@@ -80120,7 +80711,7 @@ var debugRefreshLog = (message, payload) => {
80120
80711
  var isAbortError2 = (error) => {
80121
80712
  return error instanceof DOMException && error.name === "AbortError";
80122
80713
  };
80123
- var toNumber4 = (value) => {
80714
+ var toNumber5 = (value) => {
80124
80715
  if (typeof value === "number" && Number.isFinite(value)) return value;
80125
80716
  if (typeof value === "string" && value.trim().length > 0) {
80126
80717
  const parsed = Number(value);
@@ -80129,11 +80720,11 @@ var toNumber4 = (value) => {
80129
80720
  return null;
80130
80721
  };
80131
80722
  var formatImprovementMetric = (recommendation) => {
80132
- const estimatedGain = toNumber4(recommendation.estimated_gain_pieces);
80723
+ const estimatedGain = toNumber5(recommendation.estimated_gain_pieces);
80133
80724
  if (estimatedGain !== null && estimatedGain > 0) {
80134
80725
  return `+${Math.round(estimatedGain).toLocaleString()} pcs / day`;
80135
80726
  }
80136
- const idleMinutes = toNumber4(recommendation.metrics?.idle_minutes);
80727
+ const idleMinutes = toNumber5(recommendation.metrics?.idle_minutes);
80137
80728
  if (idleMinutes !== null) {
80138
80729
  return `-${Math.round(idleMinutes)}m Idle`;
80139
80730
  }