@optifye/dashboard-core 6.11.43 → 6.11.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -5088,6 +5088,27 @@ var workspaceService = {
5088
5088
  this._workspaceVideoStreamsInFlight.set(cacheKey, fetchPromise);
5089
5089
  return fetchPromise;
5090
5090
  },
5091
+ primeWorkspaceVideoStreamsCache(params) {
5092
+ const streams = params.streams || {};
5093
+ const workspaceIds = (params.workspaceIds || []).filter(Boolean);
5094
+ const lineIds = (params.lineIds || []).filter(Boolean);
5095
+ if (!Object.keys(streams).length || !workspaceIds.length && !lineIds.length) {
5096
+ return;
5097
+ }
5098
+ const timestamp = Date.now();
5099
+ const entry = {
5100
+ streams,
5101
+ timestamp
5102
+ };
5103
+ if (workspaceIds.length) {
5104
+ const workspaceKey = workspaceIds.slice().sort().join(",");
5105
+ this._workspaceVideoStreamsCache.set(`workspaces:${workspaceKey}`, entry);
5106
+ }
5107
+ if (lineIds.length) {
5108
+ const lineKey = lineIds.slice().sort().join(",");
5109
+ this._workspaceVideoStreamsCache.set(`lines:${lineKey}`, entry);
5110
+ }
5111
+ },
5091
5112
  async getWorkspaceCameraIps(params) {
5092
5113
  const workspaceIds = (params.workspaceIds || []).filter(Boolean);
5093
5114
  const lineIds = (params.lineIds || []).filter(Boolean);
@@ -11959,8 +11980,8 @@ var useOperationalShiftKey = ({
11959
11980
  };
11960
11981
 
11961
11982
  // src/lib/stores/workspaceMetricsStore.ts
11962
- var OVERVIEW_TTL_MS = 3e4;
11963
- var DETAILED_TTL_MS = 3e4;
11983
+ var OVERVIEW_TTL_MS = 12e4;
11984
+ var DETAILED_TTL_MS = 12e4;
11964
11985
  var overviewByKey = /* @__PURE__ */ new Map();
11965
11986
  var detailedByKey = /* @__PURE__ */ new Map();
11966
11987
  var latestOverviewByWorkspace = /* @__PURE__ */ new Map();
@@ -13456,12 +13477,124 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
13456
13477
  }
13457
13478
  }
13458
13479
 
13480
+ // src/lib/utils/monitorWorkspaceMetrics.ts
13481
+ var sortWorkspaceMetrics = (left, right) => {
13482
+ if (left.line_id !== right.line_id) {
13483
+ return left.line_id.localeCompare(right.line_id);
13484
+ }
13485
+ return left.workspace_name.localeCompare(right.workspace_name, void 0, { numeric: true });
13486
+ };
13487
+ var buildLineMetricsById = (lineMetrics) => (lineMetrics || []).reduce((acc, lineMetric) => {
13488
+ const lineId = lineMetric?.line_id;
13489
+ if (!lineId) {
13490
+ return acc;
13491
+ }
13492
+ acc[lineId] = { total_workspaces: lineMetric?.total_workspaces };
13493
+ return acc;
13494
+ }, {});
13495
+ var transformMonitorWorkspaceMetrics = ({
13496
+ rows,
13497
+ companyId,
13498
+ workspaceConfig,
13499
+ appTimezone,
13500
+ lineMetrics,
13501
+ resolveShiftConfig,
13502
+ shouldOverrideShiftType,
13503
+ fallbackShiftConfig = null
13504
+ }) => {
13505
+ const effectiveWorkspaceConfig = workspaceConfig || DEFAULT_WORKSPACE_CONFIG;
13506
+ const detailTimezone = appTimezone || "Asia/Kolkata";
13507
+ const lineMetricsById = buildLineMetricsById(lineMetrics);
13508
+ return (rows || []).map((item) => {
13509
+ const lineId = String(item?.line_id || "");
13510
+ const lineShiftConfig = resolveShiftConfig?.(lineId) || fallbackShiftConfig || void 0;
13511
+ const detailedMetrics = toWorkspaceDetailedMetrics({
13512
+ data: item,
13513
+ companyId,
13514
+ workspaceConfig: effectiveWorkspaceConfig,
13515
+ shiftConfig: lineShiftConfig,
13516
+ appTimezone: detailTimezone,
13517
+ lineMetricsById,
13518
+ overrideShiftType: shouldOverrideShiftType?.(lineId) ?? false
13519
+ });
13520
+ if (detailedMetrics) {
13521
+ workspaceMetricsStore.setDetailed(detailedMetrics);
13522
+ }
13523
+ const idleTimeValue = typeof item.idle_time === "number" ? item.idle_time : Number(item.idle_time);
13524
+ const idleTimeSeconds = Number.isFinite(idleTimeValue) ? idleTimeValue : void 0;
13525
+ const pphThresholdValue = typeof item.pph_threshold === "number" ? item.pph_threshold : Number(item.pph_threshold);
13526
+ const actionFamily = normalizeActionFamily({
13527
+ actionFamily: item.action_family,
13528
+ actionType: item.action_type,
13529
+ actionName: item.action_name
13530
+ });
13531
+ const actionType = actionFamily === "assembly" || actionFamily === "output" ? actionFamily : null;
13532
+ const metric = {
13533
+ company_id: item.company_id || companyId,
13534
+ line_id: lineId,
13535
+ shift_id: item.shift_id,
13536
+ date: item.date,
13537
+ workspace_uuid: item.workspace_id,
13538
+ workspace_name: item.workspace_name,
13539
+ displayName: item.workspace_display_name || item.display_name || void 0,
13540
+ action_count: item.total_output || 0,
13541
+ pph: item.avg_pph || 0,
13542
+ pph_threshold: Number.isFinite(pphThresholdValue) ? pphThresholdValue : void 0,
13543
+ performance_score: item.performance_score || 0,
13544
+ avg_cycle_time: item.avg_cycle_time || 0,
13545
+ ideal_cycle_time: item.ideal_cycle_time || void 0,
13546
+ trend: item.trend_score === 1 ? 2 : 0,
13547
+ predicted_output: item.ideal_output || 0,
13548
+ efficiency: item.efficiency || 0,
13549
+ action_threshold: item.total_day_output || 0,
13550
+ monitoring_mode: item.monitoring_mode ?? void 0,
13551
+ idle_time: idleTimeSeconds,
13552
+ idle_time_hourly: item.idle_time_hourly ?? null,
13553
+ shift_start: item.shift_start ?? void 0,
13554
+ shift_end: item.shift_end ?? void 0,
13555
+ assembly_enabled: item.assembly_enabled ?? false,
13556
+ video_grid_metric_mode: normalizeVideoGridMetricMode(
13557
+ item.video_grid_metric_mode,
13558
+ item.assembly_enabled ?? false
13559
+ ),
13560
+ action_type: actionType,
13561
+ action_family: actionFamily,
13562
+ action_display_name: getActionDisplayName({
13563
+ displayName: item.action_display_name,
13564
+ actionFamily: item.action_family,
13565
+ actionType: item.action_type,
13566
+ actionName: item.action_name
13567
+ }),
13568
+ recent_flow_percent: item.recent_flow_percent ?? null,
13569
+ recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
13570
+ recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
13571
+ recent_flow_computed_at: item.recent_flow_computed_at ?? null,
13572
+ scheduled_break_active: item.scheduled_break_active ?? false,
13573
+ incoming_wip_current: item.incoming_wip_current ?? null,
13574
+ incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
13575
+ incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null,
13576
+ show_exclamation: item.show_exclamation ?? void 0
13577
+ };
13578
+ workspaceMetricsStore.setOverview(metric);
13579
+ return metric;
13580
+ }).sort(sortWorkspaceMetrics);
13581
+ };
13582
+
13459
13583
  // src/lib/hooks/useDashboardMetrics.ts
13460
13584
  var DEBUG_DASHBOARD_LOGS = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
13585
+ var REALTIME_REFRESH_DEBOUNCE_MS = 1500;
13586
+ var REALTIME_REFRESH_MIN_INTERVAL_MS = 5e3;
13461
13587
  var logDebug = (...args) => {
13462
13588
  if (!DEBUG_DASHBOARD_LOGS) return;
13463
13589
  console.log(...args);
13464
13590
  };
13591
+ var buildMetricsScopeKey = (lineId, lineIds) => {
13592
+ const normalizedLineIds = Array.from(new Set((lineIds || []).filter(Boolean))).sort();
13593
+ if (normalizedLineIds.length > 0) {
13594
+ return `${lineId}|${normalizedLineIds.join(",")}`;
13595
+ }
13596
+ return lineId;
13597
+ };
13465
13598
  var parseEfficiencyLegend = (legend) => {
13466
13599
  if (!legend) return null;
13467
13600
  const coerce = (value, fallback) => {
@@ -13541,6 +13674,7 @@ var useDashboardMetrics = ({
13541
13674
  const supabase = useSupabase();
13542
13675
  const [metrics2, setMetrics] = useState({ workspaceMetrics: [], lineMetrics: [] });
13543
13676
  const [metricsLineId, setMetricsLineId] = useState(lineId ?? null);
13677
+ const [metricsScopeKey, setMetricsScopeKey] = useState(() => buildMetricsScopeKey(lineId, lineIds));
13544
13678
  const [isLoading, setIsLoading] = useState(true);
13545
13679
  const [error, setError] = useState(null);
13546
13680
  const lineIdRef = useRef(lineId);
@@ -13550,8 +13684,10 @@ var useDashboardMetrics = ({
13550
13684
  const abortControllerRef = useRef(null);
13551
13685
  const lastFetchKeyRef = useRef(null);
13552
13686
  const inFlightFetchKeyRef = useRef(null);
13553
- const updateQueueRef = useRef(false);
13554
13687
  const onLineMetricsUpdateRef = useRef(onLineMetricsUpdate);
13688
+ const pendingRealtimeRefreshRef = useRef(false);
13689
+ const realtimeRefreshTimerRef = useRef(null);
13690
+ const lastRealtimeRefreshStartedAtRef = useRef(0);
13555
13691
  const shiftGroupsRef = useRef(shiftGroups);
13556
13692
  const operationalShiftKeyRef = useRef(operationalShiftKey);
13557
13693
  const configuredLineIdsRef = useRef(configuredLineIds);
@@ -13579,15 +13715,17 @@ var useDashboardMetrics = ({
13579
13715
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
13580
13716
  [entityConfig.companyId]
13581
13717
  );
13718
+ const requestedScopeKey = useMemo(
13719
+ () => buildMetricsScopeKey(lineId, isFactoryView ? targetFactoryLineIds : void 0),
13720
+ [isFactoryView, lineId, targetFactoryLineIds]
13721
+ );
13582
13722
  useEffect(() => {
13583
13723
  lineIdRef.current = lineId;
13584
- setMetrics({ workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND });
13585
- setMetricsLineId(null);
13586
13724
  setIsLoading(true);
13587
13725
  setError(null);
13588
13726
  lastFetchKeyRef.current = null;
13589
13727
  inFlightFetchKeyRef.current = null;
13590
- }, [lineId]);
13728
+ }, [lineId, requestedScopeKey]);
13591
13729
  const fetchAllMetrics = useCallback(async (options = {}) => {
13592
13730
  const { force = false } = options;
13593
13731
  const currentLineIdToUse = lineIdRef.current;
@@ -13600,7 +13738,6 @@ var useDashboardMetrics = ({
13600
13738
  companySpecificMetricsTable
13601
13739
  });
13602
13740
  if (!currentLineIdToUse || !supabase || !enabled || shiftLoading || isTimezoneLoading || companySpecificMetricsTable.includes("unknown_company")) {
13603
- updateQueueRef.current = false;
13604
13741
  if (!metrics2?.workspaceMetrics?.length && !metrics2?.lineMetrics?.length && !shiftLoading) setIsLoading(false);
13605
13742
  if (companySpecificMetricsTable.includes("unknown_company") && !error) {
13606
13743
  setError({ message: "Company ID not configured for metrics table.", code: "CONFIG_ERROR" });
@@ -13613,6 +13750,10 @@ var useDashboardMetrics = ({
13613
13750
  const usesShiftGroups = isFactory && shiftGroups.length > 0;
13614
13751
  const singleShiftDetails = usesShiftGroups ? null : shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(defaultTimezone, staticShiftConfig);
13615
13752
  const fetchKey = usesShiftGroups ? `factory|${companyId || "unknown"}|${shiftGroupsKey}` : isFactory ? `factory|${companyId || "unknown"}|${singleShiftDetails?.date}|${singleShiftDetails?.shiftId}|${targetLineIdsKey}` : `${currentLineIdToUse}|${companyId || "unknown"}|${singleShiftDetails?.date}|${singleShiftDetails?.shiftId}`;
13753
+ const responseScopeKey = buildMetricsScopeKey(
13754
+ currentLineIdToUse,
13755
+ isFactory ? targetLineIds : void 0
13756
+ );
13616
13757
  logDebug("[useDashboardMetrics] Fetch key details:", {
13617
13758
  isFactory,
13618
13759
  usesShiftGroups,
@@ -13622,14 +13763,8 @@ var useDashboardMetrics = ({
13622
13763
  singleShiftDetails,
13623
13764
  fetchKey
13624
13765
  });
13625
- if (inFlightFetchKeyRef.current === fetchKey) {
13626
- updateQueueRef.current = false;
13627
- return;
13628
- }
13629
- if (!force && lastFetchKeyRef.current === fetchKey) {
13630
- updateQueueRef.current = false;
13631
- return;
13632
- }
13766
+ if (inFlightFetchKeyRef.current === fetchKey) return;
13767
+ if (!force && lastFetchKeyRef.current === fetchKey) return;
13633
13768
  const requestId = ++activeRequestIdRef.current;
13634
13769
  const requestLineId = currentLineIdToUse;
13635
13770
  isFetchingRef.current = true;
@@ -13652,6 +13787,7 @@ var useDashboardMetrics = ({
13652
13787
  efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND
13653
13788
  });
13654
13789
  setMetricsLineId(requestLineId);
13790
+ setMetricsScopeKey(responseScopeKey);
13655
13791
  lastFetchKeyRef.current = inFlightFetchKeyRef.current;
13656
13792
  return;
13657
13793
  }
@@ -13761,89 +13897,16 @@ var useDashboardMetrics = ({
13761
13897
  }
13762
13898
  efficiencyLegend = parseEfficiencyLegend(backendData?.efficiency_legend) ?? DEFAULT_EFFICIENCY_LEGEND;
13763
13899
  }
13764
- const lineMetricsById = (allLineMetrics || []).reduce(
13765
- (acc, line) => {
13766
- const lineId2 = line?.line_id;
13767
- if (lineId2) {
13768
- acc[lineId2] = { total_workspaces: line?.total_workspaces };
13769
- }
13770
- return acc;
13771
- },
13772
- {}
13773
- );
13774
13900
  const detailTimezone = appTimezone || "Asia/Kolkata";
13775
- allWorkspaceMetrics.forEach((item) => {
13776
- const lineShiftConfig = isFactoryView ? multiLineShiftConfigMap.get(item.line_id) : shiftConfig;
13777
- const detailedMetrics = toWorkspaceDetailedMetrics({
13778
- data: item,
13779
- companyId: companyId || "",
13780
- workspaceConfig: effectiveWorkspaceConfig,
13781
- shiftConfig: lineShiftConfig || staticShiftConfig,
13782
- appTimezone: detailTimezone,
13783
- lineMetricsById,
13784
- overrideShiftType: Boolean(lineShiftConfig)
13785
- });
13786
- if (detailedMetrics) {
13787
- workspaceMetricsStore.setDetailed(detailedMetrics);
13788
- }
13789
- });
13790
- const transformedWorkspaceData = allWorkspaceMetrics.map((item) => {
13791
- const idleTimeValue = typeof item.idle_time === "number" ? item.idle_time : Number(item.idle_time);
13792
- const idleTimeSeconds = Number.isFinite(idleTimeValue) ? idleTimeValue : void 0;
13793
- const actionFamily = normalizeActionFamily({
13794
- actionFamily: item.action_family,
13795
- actionType: item.action_type,
13796
- actionName: item.action_name
13797
- });
13798
- const actionType = actionFamily === "assembly" || actionFamily === "output" ? actionFamily : null;
13799
- return {
13800
- company_id: item.company_id || companyId,
13801
- line_id: item.line_id,
13802
- shift_id: item.shift_id,
13803
- date: item.date,
13804
- workspace_uuid: item.workspace_id,
13805
- workspace_name: item.workspace_name,
13806
- displayName: item.workspace_display_name || item.display_name || void 0,
13807
- action_count: item.total_output || 0,
13808
- pph: item.avg_pph || 0,
13809
- performance_score: item.performance_score || 0,
13810
- avg_cycle_time: item.avg_cycle_time || 0,
13811
- trend: item.trend_score === 1 ? 2 : 0,
13812
- predicted_output: item.ideal_output || 0,
13813
- efficiency: item.efficiency || 0,
13814
- action_threshold: item.total_day_output || 0,
13815
- show_exclamation: item.show_exclamation ?? void 0,
13816
- monitoring_mode: item.monitoring_mode ?? void 0,
13817
- idle_time: idleTimeSeconds,
13818
- assembly_enabled: item.assembly_enabled ?? false,
13819
- video_grid_metric_mode: normalizeVideoGridMetricMode(
13820
- item.video_grid_metric_mode,
13821
- item.assembly_enabled ?? false
13822
- ),
13823
- action_type: actionType,
13824
- action_family: actionFamily,
13825
- action_display_name: getActionDisplayName({
13826
- displayName: item.action_display_name,
13827
- actionFamily: item.action_family,
13828
- actionType: item.action_type,
13829
- actionName: item.action_name
13830
- }),
13831
- recent_flow_percent: item.recent_flow_percent ?? null,
13832
- recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
13833
- recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
13834
- recent_flow_computed_at: item.recent_flow_computed_at ?? null,
13835
- incoming_wip_current: item.incoming_wip_current ?? null,
13836
- incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
13837
- incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null
13838
- };
13839
- }).sort((a, b) => {
13840
- if (a.line_id !== b.line_id) return a.line_id.localeCompare(b.line_id);
13841
- const wsNumA = parseInt(a.workspace_name?.replace(/[^0-9]/g, "") || "0");
13842
- const wsNumB = parseInt(b.workspace_name?.replace(/[^0-9]/g, "") || "0");
13843
- return wsNumA - wsNumB;
13844
- });
13845
- transformedWorkspaceData.forEach((metric) => {
13846
- workspaceMetricsStore.setOverview(metric);
13901
+ const transformedWorkspaceData = transformMonitorWorkspaceMetrics({
13902
+ rows: allWorkspaceMetrics,
13903
+ companyId: companyId || "",
13904
+ workspaceConfig: effectiveWorkspaceConfig,
13905
+ appTimezone: detailTimezone,
13906
+ lineMetrics: allLineMetrics,
13907
+ resolveShiftConfig: (metricLineId) => isFactoryView ? multiLineShiftConfigMap.get(metricLineId) || staticShiftConfig : shiftConfig || staticShiftConfig,
13908
+ shouldOverrideShiftType: (metricLineId) => isFactoryView ? Boolean(multiLineShiftConfigMap.get(metricLineId)) : Boolean(shiftConfig),
13909
+ fallbackShiftConfig: staticShiftConfig
13847
13910
  });
13848
13911
  const newMetricsState = {
13849
13912
  workspaceMetrics: transformedWorkspaceData,
@@ -13861,6 +13924,7 @@ var useDashboardMetrics = ({
13861
13924
  hydrateFromBackend(idleTimeVlmByLine);
13862
13925
  setMetrics(newMetricsState);
13863
13926
  setMetricsLineId(requestLineId);
13927
+ setMetricsScopeKey(responseScopeKey);
13864
13928
  lastFetchKeyRef.current = inFlightFetchKeyRef.current;
13865
13929
  } catch (err) {
13866
13930
  if (abortController.signal.aborted || err?.name === "AbortError") {
@@ -13883,9 +13947,24 @@ var useDashboardMetrics = ({
13883
13947
  if (requestId === activeRequestIdRef.current) {
13884
13948
  setIsLoading(false);
13885
13949
  isFetchingRef.current = false;
13886
- updateQueueRef.current = false;
13887
13950
  activeFetchLineIdRef.current = null;
13888
13951
  inFlightFetchKeyRef.current = null;
13952
+ if (pendingRealtimeRefreshRef.current) {
13953
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
13954
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS - elapsedSinceLastRealtimeRefresh);
13955
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS, minIntervalRemaining);
13956
+ if (realtimeRefreshTimerRef.current === null) {
13957
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
13958
+ realtimeRefreshTimerRef.current = null;
13959
+ if (!pendingRealtimeRefreshRef.current || isFetchingRef.current || inFlightFetchKeyRef.current) {
13960
+ return;
13961
+ }
13962
+ pendingRealtimeRefreshRef.current = false;
13963
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
13964
+ fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13965
+ }, nextDelay);
13966
+ }
13967
+ }
13889
13968
  }
13890
13969
  }
13891
13970
  }, [
@@ -13915,20 +13994,59 @@ var useDashboardMetrics = ({
13915
13994
  useEffect(() => {
13916
13995
  fetchAllMetricsRef.current = fetchAllMetrics;
13917
13996
  }, [fetchAllMetrics]);
13918
- const queueUpdate = useCallback(() => {
13919
- if (updateQueueRef.current || !supabase) {
13920
- if (updateQueueRef.current) {
13921
- logDebug("[useDashboardMetrics] queueUpdate skipped: update already queued");
13997
+ const clearRealtimeRefreshTimer = useCallback(() => {
13998
+ if (realtimeRefreshTimerRef.current !== null) {
13999
+ window.clearTimeout(realtimeRefreshTimerRef.current);
14000
+ realtimeRefreshTimerRef.current = null;
14001
+ }
14002
+ }, []);
14003
+ const scheduleRealtimeRefresh = useCallback(() => {
14004
+ if (!enabled || !supabase) {
14005
+ pendingRealtimeRefreshRef.current = false;
14006
+ clearRealtimeRefreshTimer();
14007
+ return;
14008
+ }
14009
+ if (!pendingRealtimeRefreshRef.current || realtimeRefreshTimerRef.current !== null) {
14010
+ return;
14011
+ }
14012
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
14013
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS - elapsedSinceLastRealtimeRefresh);
14014
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS, minIntervalRemaining);
14015
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
14016
+ realtimeRefreshTimerRef.current = null;
14017
+ if (!pendingRealtimeRefreshRef.current) {
14018
+ return;
14019
+ }
14020
+ if (isFetchingRef.current || inFlightFetchKeyRef.current) {
14021
+ scheduleRealtimeRefresh();
14022
+ return;
13922
14023
  }
14024
+ pendingRealtimeRefreshRef.current = false;
14025
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
14026
+ fetchAllMetricsRef.current({ force: true, reason: "subscription" });
14027
+ }, nextDelay);
14028
+ }, [clearRealtimeRefreshTimer, enabled, supabase]);
14029
+ useEffect(() => {
14030
+ return () => {
14031
+ clearRealtimeRefreshTimer();
14032
+ pendingRealtimeRefreshRef.current = false;
14033
+ };
14034
+ }, [clearRealtimeRefreshTimer]);
14035
+ const queueUpdate = useCallback(() => {
14036
+ if (!enabled) {
14037
+ logDebug("[useDashboardMetrics] queueUpdate skipped: metrics disabled");
14038
+ return;
14039
+ }
14040
+ if (!supabase) {
13923
14041
  if (!supabase) {
13924
14042
  logDebug("[useDashboardMetrics] queueUpdate skipped: supabase not ready");
13925
14043
  }
13926
14044
  return;
13927
14045
  }
13928
- logDebug("[useDashboardMetrics] queueUpdate triggered from realtime");
13929
- updateQueueRef.current = true;
13930
- fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13931
- }, [supabase, enabled]);
14046
+ pendingRealtimeRefreshRef.current = true;
14047
+ logDebug("[useDashboardMetrics] queueUpdate queued realtime refresh");
14048
+ scheduleRealtimeRefresh();
14049
+ }, [enabled, scheduleRealtimeRefresh, supabase]);
13932
14050
  useEffect(() => {
13933
14051
  if (enabled && lineId && supabase && !shiftLoading && !isTimezoneLoading) {
13934
14052
  fetchAllMetrics({ reason: "line-change" });
@@ -14048,7 +14166,7 @@ var useDashboardMetrics = ({
14048
14166
  { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
14049
14167
  (payload) => {
14050
14168
  const payloadData = payload.new || payload.old;
14051
- console.log("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
14169
+ logDebug("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
14052
14170
  eventType: payload.eventType,
14053
14171
  lineId: payloadData?.line_id,
14054
14172
  workspaceId: payloadData?.workspace_id,
@@ -14070,14 +14188,14 @@ var useDashboardMetrics = ({
14070
14188
  shiftId: payloadData?.shift_id
14071
14189
  });
14072
14190
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
14073
- console.log("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
14191
+ logDebug("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
14074
14192
  queueUpdate();
14075
14193
  } else {
14076
- console.log("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
14194
+ logDebug("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
14077
14195
  }
14078
14196
  }
14079
14197
  ).subscribe((status) => {
14080
- console.log("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
14198
+ logDebug("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
14081
14199
  channel: wsChannelName,
14082
14200
  status,
14083
14201
  table: companySpecificMetricsTable,
@@ -14095,7 +14213,7 @@ var useDashboardMetrics = ({
14095
14213
  { event: "*", schema, table: configuredLineMetricsTable, filter: filter2 },
14096
14214
  (payload) => {
14097
14215
  const payloadData = payload.new || payload.old;
14098
- console.log("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
14216
+ logDebug("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
14099
14217
  eventType: payload.eventType,
14100
14218
  lineId: payloadData?.line_id,
14101
14219
  date: payloadData?.date,
@@ -14115,15 +14233,15 @@ var useDashboardMetrics = ({
14115
14233
  shiftId: payloadData?.shift_id
14116
14234
  });
14117
14235
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
14118
- console.log("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
14236
+ logDebug("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
14119
14237
  queueUpdate();
14120
14238
  onLineMetricsUpdateRef.current?.();
14121
14239
  } else {
14122
- console.log("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
14240
+ logDebug("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
14123
14241
  }
14124
14242
  }
14125
14243
  ).subscribe((status) => {
14126
- console.log("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
14244
+ logDebug("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
14127
14245
  channel: lmChannelName,
14128
14246
  status,
14129
14247
  table: configuredLineMetricsTable,
@@ -14219,7 +14337,7 @@ var useDashboardMetrics = ({
14219
14337
  { event: "*", schema, table, filter: filter2 },
14220
14338
  (payload) => {
14221
14339
  const payloadData = payload.new || payload.old;
14222
- console.log(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
14340
+ logDebug(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
14223
14341
  eventType: payload.eventType,
14224
14342
  table: payload.table,
14225
14343
  lineId: payloadData?.line_id,
@@ -14243,14 +14361,14 @@ var useDashboardMetrics = ({
14243
14361
  shiftId: payloadData?.shift_id
14244
14362
  });
14245
14363
  if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
14246
- console.log(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
14364
+ logDebug(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
14247
14365
  callback();
14248
14366
  } else {
14249
- console.log(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
14367
+ logDebug(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
14250
14368
  }
14251
14369
  }
14252
14370
  ).subscribe((status) => {
14253
- console.log(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
14371
+ logDebug(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
14254
14372
  channel: channelName,
14255
14373
  status,
14256
14374
  table,
@@ -14299,14 +14417,17 @@ var useDashboardMetrics = ({
14299
14417
  lineId
14300
14418
  // NOTE: userAccessibleLineIds removed - accessed via ref
14301
14419
  ]);
14302
- const isMetricsForActiveLine = metricsLineId === lineId;
14303
- const safeMetrics = isMetricsForActiveLine ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
14420
+ const isCurrentScopeResolved = metricsScopeKey === requestedScopeKey;
14421
+ const hasLastGoodMetrics = metrics2.workspaceMetrics.length > 0 || metrics2.lineMetrics.length > 0;
14422
+ const canReuseLastGoodMetrics = hasLastGoodMetrics && !isCurrentScopeResolved && (isLoading || !!error);
14423
+ const safeMetrics = isCurrentScopeResolved || canReuseLastGoodMetrics ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
14304
14424
  return {
14305
14425
  workspaceMetrics: safeMetrics?.workspaceMetrics || [],
14306
14426
  lineMetrics: safeMetrics?.lineMetrics || [],
14307
14427
  efficiencyLegend: safeMetrics?.efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND,
14308
14428
  metadata: safeMetrics?.metadata,
14309
- isLoading: enabled ? isLoading || !isMetricsForActiveLine : false,
14429
+ isLoading: enabled ? isLoading || !isCurrentScopeResolved && !canReuseLastGoodMetrics : false,
14430
+ isCurrentScopeResolved,
14310
14431
  error,
14311
14432
  refetch: () => {
14312
14433
  if (!enabled) {
@@ -16754,6 +16875,7 @@ var isInitialized = false;
16754
16875
  var isInitializing = false;
16755
16876
  var initializedWithLineIds = [];
16756
16877
  var missingLineContextWarnings = /* @__PURE__ */ new Set();
16878
+ var lineLoadPromises = /* @__PURE__ */ new Map();
16757
16879
  var initializationPromise = null;
16758
16880
  var workspaceDisplayNamesListeners = /* @__PURE__ */ new Set();
16759
16881
  var notifyWorkspaceDisplayNamesListeners = (changedLineId) => {
@@ -16769,6 +16891,30 @@ var subscribeWorkspaceDisplayNames = (listener) => {
16769
16891
  workspaceDisplayNamesListeners.add(listener);
16770
16892
  return () => workspaceDisplayNamesListeners.delete(listener);
16771
16893
  };
16894
+ var storeLineDisplayNames = (lineId, lineDisplayNamesMap) => {
16895
+ runtimeWorkspaceDisplayNames[lineId] = {};
16896
+ lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16897
+ runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16898
+ });
16899
+ };
16900
+ var ensureLineWorkspaceDisplayNamesLoaded = async (lineId) => {
16901
+ if (!lineId || runtimeWorkspaceDisplayNames[lineId]) {
16902
+ return;
16903
+ }
16904
+ const existingPromise = lineLoadPromises.get(lineId);
16905
+ if (existingPromise) {
16906
+ await existingPromise;
16907
+ return;
16908
+ }
16909
+ const loadPromise = workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16910
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
16911
+ notifyWorkspaceDisplayNamesListeners(lineId);
16912
+ }).finally(() => {
16913
+ lineLoadPromises.delete(lineId);
16914
+ });
16915
+ lineLoadPromises.set(lineId, loadPromise);
16916
+ await loadPromise;
16917
+ };
16772
16918
  var getAllWorkspaceDisplayNamesSnapshot = (lineId) => {
16773
16919
  if (lineId && runtimeWorkspaceDisplayNames[lineId]) {
16774
16920
  return { ...runtimeWorkspaceDisplayNames[lineId] };
@@ -16833,6 +16979,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
16833
16979
  }
16834
16980
  console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
16835
16981
  runtimeWorkspaceDisplayNames = {};
16982
+ lineLoadPromises.clear();
16836
16983
  if (targetLineIds.length > 0) {
16837
16984
  const results = await Promise.all(
16838
16985
  targetLineIds.map(async (lineId) => {
@@ -16842,10 +16989,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
16842
16989
  })
16843
16990
  );
16844
16991
  results.forEach(({ lineId, lineDisplayNamesMap }) => {
16845
- runtimeWorkspaceDisplayNames[lineId] = {};
16846
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16847
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16848
- });
16992
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
16849
16993
  console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16850
16994
  });
16851
16995
  } else {
@@ -16877,13 +17021,8 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
16877
17021
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16878
17022
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
16879
17023
  try {
16880
- const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
16881
- runtimeWorkspaceDisplayNames[lineId] = {};
16882
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16883
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16884
- });
16885
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16886
- notifyWorkspaceDisplayNamesListeners(lineId);
17024
+ await ensureLineWorkspaceDisplayNamesLoaded(lineId);
17025
+ console.log(`\u2705 Added workspaces for line ${lineId}`);
16887
17026
  } catch (error) {
16888
17027
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16889
17028
  }
@@ -16896,13 +17035,8 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
16896
17035
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16897
17036
  console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
16898
17037
  try {
16899
- const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
16900
- runtimeWorkspaceDisplayNames[lineId] = {};
16901
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16902
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16903
- });
16904
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16905
- notifyWorkspaceDisplayNamesListeners(lineId);
17038
+ await ensureLineWorkspaceDisplayNamesLoaded(lineId);
17039
+ console.log(`\u2705 Added workspaces for line ${lineId}`);
16906
17040
  } catch (error) {
16907
17041
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16908
17042
  }
@@ -16939,13 +17073,8 @@ var getWorkspaceDisplayName = (workspaceId, lineId) => {
16939
17073
  }
16940
17074
  if (isInitialized && lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16941
17075
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching its workspaces...`);
16942
- workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16943
- runtimeWorkspaceDisplayNames[lineId] = {};
16944
- lineDisplayNamesMap.forEach((displayName2, workspaceId2) => {
16945
- runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
16946
- });
16947
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
16948
- notifyWorkspaceDisplayNamesListeners(lineId);
17076
+ ensureLineWorkspaceDisplayNamesLoaded(lineId).then(() => {
17077
+ console.log(`\u2705 Added workspaces for line ${lineId} to cache`);
16949
17078
  }).catch((error) => {
16950
17079
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16951
17080
  });
@@ -16987,13 +17116,8 @@ var getShortWorkspaceDisplayName = (workspaceId, lineId) => {
16987
17116
  }
16988
17117
  if (isInitialized && lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16989
17118
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching its workspaces...`);
16990
- workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16991
- runtimeWorkspaceDisplayNames[lineId] = {};
16992
- lineDisplayNamesMap.forEach((displayName2, workspaceId2) => {
16993
- runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
16994
- });
16995
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
16996
- notifyWorkspaceDisplayNamesListeners(lineId);
17119
+ ensureLineWorkspaceDisplayNamesLoaded(lineId).then(() => {
17120
+ console.log(`\u2705 Added workspaces for line ${lineId} to cache`);
16997
17121
  }).catch((error) => {
16998
17122
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16999
17123
  });
@@ -17073,6 +17197,7 @@ var refreshWorkspaceDisplayNames = async (companyId) => {
17073
17197
  workspaceService.clearWorkspaceDisplayNamesCache();
17074
17198
  runtimeWorkspaceDisplayNames = {};
17075
17199
  isInitialized = false;
17200
+ lineLoadPromises.clear();
17076
17201
  await initializeWorkspaceDisplayNames();
17077
17202
  notifyWorkspaceDisplayNamesListeners();
17078
17203
  };
@@ -17082,6 +17207,7 @@ var clearWorkspaceDisplayNamesCache = () => {
17082
17207
  isInitialized = false;
17083
17208
  isInitializing = false;
17084
17209
  initializedWithLineIds = [];
17210
+ lineLoadPromises.clear();
17085
17211
  initializationPromise = null;
17086
17212
  notifyWorkspaceDisplayNamesListeners();
17087
17213
  };
@@ -17211,7 +17337,16 @@ var useWorkspaceDisplayNamesMap = (workspaceIds, lineId, companyId) => {
17211
17337
  refetch: fetchDisplayNames
17212
17338
  };
17213
17339
  };
17214
- var useWorkspaceVideoStreams = (workspaces) => {
17340
+ var useWorkspaceVideoStreams = (workspaces, options = {}) => {
17341
+ const explicitLineIdsKey = useMemo(() => {
17342
+ const ids = /* @__PURE__ */ new Set();
17343
+ for (const lineId of options.lineIds || []) {
17344
+ if (lineId) {
17345
+ ids.add(lineId);
17346
+ }
17347
+ }
17348
+ return Array.from(ids).sort().join(",");
17349
+ }, [options.lineIds]);
17215
17350
  const workspaceIdsKey = useMemo(() => {
17216
17351
  const ids = /* @__PURE__ */ new Set();
17217
17352
  for (const workspace of workspaces) {
@@ -17221,7 +17356,7 @@ var useWorkspaceVideoStreams = (workspaces) => {
17221
17356
  }
17222
17357
  return Array.from(ids).sort().join(",");
17223
17358
  }, [workspaces]);
17224
- const lineIdsKey = useMemo(() => {
17359
+ const inferredLineIdsKey = useMemo(() => {
17225
17360
  const ids = /* @__PURE__ */ new Set();
17226
17361
  for (const workspace of workspaces) {
17227
17362
  if (workspace.line_id) {
@@ -17230,18 +17365,19 @@ var useWorkspaceVideoStreams = (workspaces) => {
17230
17365
  }
17231
17366
  return Array.from(ids).sort().join(",");
17232
17367
  }, [workspaces]);
17368
+ const lineIdsKey = explicitLineIdsKey || inferredLineIdsKey;
17233
17369
  const requestKey = useMemo(() => {
17234
- if (workspaceIdsKey) {
17235
- return `workspaces:${workspaceIdsKey}`;
17236
- }
17237
17370
  if (lineIdsKey) {
17238
17371
  return `lines:${lineIdsKey}`;
17239
17372
  }
17373
+ if (workspaceIdsKey) {
17374
+ return `workspaces:${workspaceIdsKey}`;
17375
+ }
17240
17376
  return "";
17241
17377
  }, [workspaceIdsKey, lineIdsKey]);
17242
17378
  const workspaceIds = useMemo(
17243
- () => workspaceIdsKey ? workspaceIdsKey.split(",") : [],
17244
- [workspaceIdsKey]
17379
+ () => lineIdsKey ? [] : workspaceIdsKey ? workspaceIdsKey.split(",") : [],
17380
+ [lineIdsKey, workspaceIdsKey]
17245
17381
  );
17246
17382
  const lineIds = useMemo(
17247
17383
  () => lineIdsKey ? lineIdsKey.split(",") : [],
@@ -18785,6 +18921,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18785
18921
  const supabase = useSupabase();
18786
18922
  const databaseConfig = useDatabaseConfig();
18787
18923
  const refreshInterval = options.refreshInterval ?? DEFAULT_REFRESH_INTERVAL_MS;
18924
+ const enabled = options.enabled ?? true;
18788
18925
  const workspaceIdsKey = useMemo(() => {
18789
18926
  const ids = Array.from(new Set(workspaceIds.filter(Boolean)));
18790
18927
  return ids.sort().join(",");
@@ -18795,7 +18932,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18795
18932
  const isFetchingRef = useRef(false);
18796
18933
  const refreshIntervalRef = useRef(null);
18797
18934
  const fetchLastSeen = useCallback(async () => {
18798
- if (!supabase || !workspaceIdsKey || isFetchingRef.current) return;
18935
+ if (!enabled || !supabase || !workspaceIdsKey || isFetchingRef.current) return;
18799
18936
  const healthTable = databaseConfig?.tables?.workspace_health || "workspace_health_status";
18800
18937
  try {
18801
18938
  isFetchingRef.current = true;
@@ -18827,12 +18964,18 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18827
18964
  setIsLoading(false);
18828
18965
  isFetchingRef.current = false;
18829
18966
  }
18830
- }, [supabase, workspaceIdsKey, databaseConfig?.tables?.workspace_health]);
18967
+ }, [databaseConfig?.tables?.workspace_health, enabled, supabase, workspaceIdsKey]);
18831
18968
  useEffect(() => {
18969
+ if (!enabled) {
18970
+ setIsLoading(false);
18971
+ setError(null);
18972
+ setLastSeenByWorkspaceId({});
18973
+ return;
18974
+ }
18832
18975
  fetchLastSeen();
18833
- }, [fetchLastSeen]);
18976
+ }, [enabled, fetchLastSeen]);
18834
18977
  useEffect(() => {
18835
- if (!refreshInterval || !workspaceIdsKey) return;
18978
+ if (!enabled || !refreshInterval || !workspaceIdsKey) return;
18836
18979
  refreshIntervalRef.current = setInterval(() => {
18837
18980
  fetchLastSeen();
18838
18981
  }, refreshInterval);
@@ -18842,7 +18985,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18842
18985
  refreshIntervalRef.current = null;
18843
18986
  }
18844
18987
  };
18845
- }, [fetchLastSeen, refreshInterval, workspaceIdsKey]);
18988
+ }, [enabled, fetchLastSeen, refreshInterval, workspaceIdsKey]);
18846
18989
  return {
18847
18990
  lastSeenByWorkspaceId,
18848
18991
  isLoading,
@@ -34675,7 +34818,7 @@ var VideoCard = React143__default.memo(({
34675
34818
  });
34676
34819
  const showOffline = Boolean(isStreamStale);
34677
34820
  const lastSeenText = lastSeenLabel || "Unknown";
34678
- const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
34821
+ const workspaceDisplayName = displayName || workspace.displayName || workspace.workspace_name;
34679
34822
  const videoGridMetricValue = getVideoGridMetricValue(workspace);
34680
34823
  const videoGridDisplayValue = getVideoGridDisplayValue(workspace, effectiveLegend, displayMinuteBucket);
34681
34824
  const videoGridColorState = getVideoGridColorState(workspace, effectiveLegend);
@@ -34888,7 +35031,10 @@ var VideoGridView = React143__default.memo(({
34888
35031
  }
34889
35032
  return Array.from(ids);
34890
35033
  }, [workspaces]);
34891
- const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds);
35034
+ const healthFetchEnabled = !videoStreamsLoading && workspaces.length > 0;
35035
+ const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds, {
35036
+ enabled: healthFetchEnabled
35037
+ });
34892
35038
  useEffect(() => {
34893
35039
  const sample = workspaces.slice(0, 3).map((workspace) => ({
34894
35040
  id: workspace.workspace_uuid || workspace.workspace_name,
@@ -34987,6 +35133,9 @@ var VideoGridView = React143__default.memo(({
34987
35133
  }, [sortedWorkspaces, lineOrder, lineNames]);
34988
35134
  lineGroups.length > 1;
34989
35135
  const streamsReady = !videoStreamsLoading;
35136
+ const resolveWorkspaceDisplayName = useCallback((workspace) => {
35137
+ return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
35138
+ }, [displayNames]);
34990
35139
  const calculateOptimalGrid = useCallback(() => {
34991
35140
  if (!containerRef.current) return;
34992
35141
  const containerPadding = 16;
@@ -35117,11 +35266,11 @@ var VideoGridView = React143__default.memo(({
35117
35266
  efficiency: workspace.efficiency,
35118
35267
  action_count: workspace.action_count
35119
35268
  });
35120
- const displayName = workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
35269
+ const displayName = resolveWorkspaceDisplayName(workspace);
35121
35270
  const currentPath = (router.asPath || "/").split("#")[0];
35122
35271
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id, currentPath);
35123
35272
  router.push(`/workspace/${workspaceId}${navParams}`);
35124
- }, [router, prewarmClipsInit]);
35273
+ }, [resolveWorkspaceDisplayName, router, prewarmClipsInit]);
35125
35274
  const handleStreamError = useCallback((workspaceId, options) => {
35126
35275
  const isR2Stream = options?.isR2Stream ?? false;
35127
35276
  const hasFallback = Boolean(options?.fallbackUrl);
@@ -35230,7 +35379,7 @@ var VideoGridView = React143__default.memo(({
35230
35379
  legend: effectiveLegend,
35231
35380
  cropping: card.workspaceCropping,
35232
35381
  canvasFps: effectiveCanvasFps,
35233
- displayName: displayNames[`${card.workspace.line_id}_${card.workspace.workspace_name}`] || getWorkspaceDisplayName(card.workspace.workspace_name, card.workspace.line_id),
35382
+ displayName: resolveWorkspaceDisplayName(card.workspace),
35234
35383
  lastSeenLabel: card.lastSeenLabel,
35235
35384
  useRAF: effectiveUseRAF,
35236
35385
  displayMinuteBucket,
@@ -35242,16 +35391,15 @@ var VideoGridView = React143__default.memo(({
35242
35391
  },
35243
35392
  card.workspaceKey
35244
35393
  ), [
35245
- displayNames,
35246
35394
  displayMinuteBucket,
35247
35395
  effectiveCanvasFps,
35248
35396
  effectiveLegend,
35249
35397
  effectiveUseRAF,
35250
- getWorkspaceDisplayName,
35251
35398
  handleStreamError,
35252
35399
  handleWorkspaceClick,
35253
35400
  onWorkspaceHover,
35254
35401
  onWorkspaceHoverEnd,
35402
+ resolveWorkspaceDisplayName,
35255
35403
  selectedLine
35256
35404
  ]);
35257
35405
  return /* @__PURE__ */ jsx("div", { className: `relative overflow-hidden h-full w-full bg-slate-50/30 ${className}`, children: /* @__PURE__ */ jsx(
@@ -35337,6 +35485,9 @@ var MapGridView = React143__default.memo(({
35337
35485
  prewarmInFlightRef.current.delete(workspaceId);
35338
35486
  });
35339
35487
  }, [dashboardConfig, timezone, supabase]);
35488
+ const resolveWorkspaceDisplayName = useCallback((workspace) => {
35489
+ return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
35490
+ }, [displayNames]);
35340
35491
  const handleWorkspaceClick = useCallback((workspace) => {
35341
35492
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
35342
35493
  prewarmClipsInit(workspace);
@@ -35348,12 +35499,11 @@ var MapGridView = React143__default.memo(({
35348
35499
  efficiency: workspace.efficiency,
35349
35500
  action_count: workspace.action_count
35350
35501
  });
35351
- const displayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
35352
- getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
35502
+ const displayName = resolveWorkspaceDisplayName(workspace);
35353
35503
  const currentPath = (router.asPath || "/").split("#")[0];
35354
35504
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id, currentPath);
35355
35505
  router.push(`/workspace/${workspaceId}${navParams}`);
35356
- }, [router, displayNames, prewarmClipsInit]);
35506
+ }, [resolveWorkspaceDisplayName, router, prewarmClipsInit]);
35357
35507
  const activePositions = useMemo(() => {
35358
35508
  return workspacePositions.filter((pos) => {
35359
35509
  const wsKey = pos.id.toUpperCase();
@@ -35383,7 +35533,18 @@ var MapGridView = React143__default.memo(({
35383
35533
  workspace_uuid: position.id,
35384
35534
  // Use config ID as temporary UUID
35385
35535
  line_id: "",
35536
+ // Empty line ID
35537
+ company_id: "",
35538
+ shift_id: 0,
35539
+ date: "",
35386
35540
  efficiency: 0,
35541
+ performance_score: 0,
35542
+ action_count: 0,
35543
+ pph: 0,
35544
+ avg_cycle_time: 0,
35545
+ trend: 0,
35546
+ predicted_output: 0,
35547
+ action_threshold: 0,
35387
35548
  show_exclamation: false
35388
35549
  };
35389
35550
  const workspaceId = effectiveWorkspace.workspace_uuid || effectiveWorkspace.workspace_name;
@@ -35392,7 +35553,7 @@ var MapGridView = React143__default.memo(({
35392
35553
  const performanceColor = isInactivePlaceholder || isConveyorRow ? "bg-slate-200 border-slate-300 text-slate-500" : getPerformanceColor(effectiveWorkspace.efficiency);
35393
35554
  !isInactivePlaceholder && !isConveyorRow && (effectiveWorkspace.show_exclamation ?? (effectiveWorkspace.efficiency < 50 && effectiveWorkspace.efficiency >= 10));
35394
35555
  const configLabel = position.label;
35395
- const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : displayNames[`${effectiveWorkspace.line_id}_${effectiveWorkspace.workspace_name}`] || getWorkspaceDisplayName(effectiveWorkspace.workspace_name, effectiveWorkspace.line_id) || position.id);
35556
+ const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : resolveWorkspaceDisplayName(effectiveWorkspace) || position.id);
35396
35557
  return /* @__PURE__ */ jsx(
35397
35558
  motion.div,
35398
35559
  {
@@ -39359,9 +39520,12 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
39359
39520
  var LoadingOverlay = ({
39360
39521
  isVisible,
39361
39522
  message = "Loading...",
39362
- className
39523
+ className,
39524
+ contentClassName,
39525
+ contentVariant = "card"
39363
39526
  }) => {
39364
39527
  if (!isVisible) return null;
39528
+ const contentClasses = contentVariant === "plain" ? "flex flex-col items-center justify-center" : "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl";
39365
39529
  return /* @__PURE__ */ jsx(
39366
39530
  motion.div,
39367
39531
  {
@@ -39372,7 +39536,7 @@ var LoadingOverlay = ({
39372
39536
  className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
39373
39537
  "aria-modal": "true",
39374
39538
  role: "dialog",
39375
- children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message }) })
39539
+ children: /* @__PURE__ */ jsx("div", { className: `${contentClasses} ${contentClassName || ""}`, children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message }) })
39376
39540
  }
39377
39541
  );
39378
39542
  };
@@ -60702,6 +60866,325 @@ var HelpView = ({
60702
60866
  };
60703
60867
  var AuthenticatedHelpView = withAuth(HelpView);
60704
60868
  var HelpView_default = HelpView;
60869
+ var transformActiveBreaks = (activeBreaksByLine) => {
60870
+ if (!activeBreaksByLine) return [];
60871
+ return Object.values(activeBreaksByLine).flat().map((item) => ({
60872
+ lineId: item.line_id,
60873
+ shiftName: item.shift_name || "",
60874
+ startTime: item.start_time,
60875
+ endTime: item.end_time,
60876
+ duration: item.duration || 0,
60877
+ remarks: item.remarks || "",
60878
+ elapsedMinutes: item.elapsed_minutes || 0,
60879
+ remainingMinutes: item.remaining_minutes || 0
60880
+ })).sort((left, right) => {
60881
+ if (left.lineId !== right.lineId) return left.lineId.localeCompare(right.lineId);
60882
+ if (left.shiftName !== right.shiftName) return left.shiftName.localeCompare(right.shiftName);
60883
+ return left.startTime.localeCompare(right.startTime);
60884
+ });
60885
+ };
60886
+ var createEmptyState = () => ({
60887
+ requestKey: null,
60888
+ resolvedScope: [],
60889
+ scopeKey: null,
60890
+ lines: [],
60891
+ workspaceMetrics: [],
60892
+ lineMetrics: [],
60893
+ kpiTrend: null,
60894
+ activeBreaks: [],
60895
+ videoStreamsByWorkspaceId: {},
60896
+ efficiencyLegend: null,
60897
+ metadata: {}
60898
+ });
60899
+ var normalizeMetadata = (metadata) => ({
60900
+ hasFlowBuffers: Boolean(metadata?.has_flow_buffers),
60901
+ idleTimeVlmByLine: metadata?.idle_time_vlm_by_line ?? {},
60902
+ generatedAt: metadata?.generated_at,
60903
+ cacheStatus: metadata?.cache_status,
60904
+ warnings: metadata?.warnings ?? []
60905
+ });
60906
+ var useLiveMonitorBootstrap = ({
60907
+ lineIds,
60908
+ companyId,
60909
+ enabled = true,
60910
+ appTimezone,
60911
+ workspaceConfig,
60912
+ fallbackShiftConfig,
60913
+ lineShiftConfigs
60914
+ }) => {
60915
+ const supabase = useSupabase();
60916
+ const entityConfig = useEntityConfig();
60917
+ const { hydrateFromBackend } = useIdleTimeVlmConfig();
60918
+ const resolvedCompanyId = companyId || entityConfig?.companyId;
60919
+ const effectiveWorkspaceConfig = workspaceConfig || DEFAULT_WORKSPACE_CONFIG;
60920
+ const effectiveTimezone = appTimezone || "Asia/Kolkata";
60921
+ const rawLineIdsKey = (lineIds || []).filter(Boolean).join(",");
60922
+ const normalizedLineIds = useMemo(
60923
+ () => Array.from(new Set(rawLineIdsKey ? rawLineIdsKey.split(",") : [])),
60924
+ [rawLineIdsKey]
60925
+ );
60926
+ const requestKey = useMemo(
60927
+ () => normalizedLineIds.slice().sort().join(","),
60928
+ [normalizedLineIds]
60929
+ );
60930
+ const [state, setState] = useState(() => createEmptyState());
60931
+ const [isLoading, setIsLoading] = useState(false);
60932
+ const [error, setError] = useState(null);
60933
+ const activeRequestIdRef = useRef(0);
60934
+ const fetchBootstrap = useCallback(async (force = false) => {
60935
+ if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
60936
+ return;
60937
+ }
60938
+ const requestId = ++activeRequestIdRef.current;
60939
+ setIsLoading(true);
60940
+ setError(null);
60941
+ try {
60942
+ const searchParams = new URLSearchParams();
60943
+ searchParams.set("company_id", resolvedCompanyId);
60944
+ searchParams.set("line_ids", normalizedLineIds.join(","));
60945
+ if (force) {
60946
+ searchParams.set("force_refresh", "true");
60947
+ }
60948
+ const response = await fetchBackendJson(
60949
+ supabase,
60950
+ `/api/dashboard/monitor-bootstrap?${searchParams.toString()}`,
60951
+ {
60952
+ method: "GET",
60953
+ timeoutMs: 2e4,
60954
+ retries: 0,
60955
+ dedupeKey: `monitor-bootstrap::${requestKey}::${force ? "force" : "cached"}`
60956
+ }
60957
+ );
60958
+ if (requestId !== activeRequestIdRef.current) {
60959
+ return;
60960
+ }
60961
+ const workspaceMetrics = transformMonitorWorkspaceMetrics({
60962
+ rows: response.workspace_metrics || [],
60963
+ companyId: resolvedCompanyId,
60964
+ workspaceConfig: effectiveWorkspaceConfig,
60965
+ appTimezone: effectiveTimezone,
60966
+ lineMetrics: response.line_metrics || [],
60967
+ resolveShiftConfig: (lineId) => lineShiftConfigs?.get(lineId) || fallbackShiftConfig,
60968
+ shouldOverrideShiftType: (lineId) => Boolean(lineShiftConfigs?.get(lineId)),
60969
+ fallbackShiftConfig
60970
+ });
60971
+ const activeBreaks = transformActiveBreaks(response.active_breaks_by_line);
60972
+ const metadata = normalizeMetadata(response.metadata);
60973
+ const workspaceIds = workspaceMetrics.map((metric) => metric.workspace_uuid).filter((workspaceId) => Boolean(workspaceId));
60974
+ const videoStreamsByWorkspaceId = response.video_streams_by_workspace_id || {};
60975
+ if (metadata.idleTimeVlmByLine) {
60976
+ hydrateFromBackend(metadata.idleTimeVlmByLine);
60977
+ }
60978
+ workspaceService.primeWorkspaceVideoStreamsCache({
60979
+ streams: videoStreamsByWorkspaceId,
60980
+ workspaceIds,
60981
+ lineIds: normalizedLineIds
60982
+ });
60983
+ setState({
60984
+ requestKey,
60985
+ resolvedScope: response.resolved_scope || [],
60986
+ scopeKey: response.scope_key || null,
60987
+ lines: response.lines || [],
60988
+ workspaceMetrics,
60989
+ lineMetrics: response.line_metrics || [],
60990
+ kpiTrend: response.kpi_trend || null,
60991
+ activeBreaks,
60992
+ videoStreamsByWorkspaceId,
60993
+ efficiencyLegend: response.efficiency_legend || null,
60994
+ metadata
60995
+ });
60996
+ } catch (fetchError) {
60997
+ if (requestId !== activeRequestIdRef.current) {
60998
+ return;
60999
+ }
61000
+ setError(fetchError instanceof Error ? fetchError : new Error("Failed to load live monitor bootstrap"));
61001
+ } finally {
61002
+ if (requestId === activeRequestIdRef.current) {
61003
+ setIsLoading(false);
61004
+ }
61005
+ }
61006
+ }, [
61007
+ enabled,
61008
+ supabase,
61009
+ resolvedCompanyId,
61010
+ normalizedLineIds,
61011
+ requestKey,
61012
+ hydrateFromBackend,
61013
+ effectiveWorkspaceConfig,
61014
+ effectiveTimezone,
61015
+ lineShiftConfigs,
61016
+ fallbackShiftConfig
61017
+ ]);
61018
+ useEffect(() => {
61019
+ if (!enabled) {
61020
+ setIsLoading(false);
61021
+ return;
61022
+ }
61023
+ if (!resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
61024
+ setState(createEmptyState());
61025
+ setIsLoading(false);
61026
+ setError(null);
61027
+ return;
61028
+ }
61029
+ void fetchBootstrap(false);
61030
+ }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
61031
+ useEffect(() => {
61032
+ if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
61033
+ return void 0;
61034
+ }
61035
+ let intervalId = null;
61036
+ let timeoutId = null;
61037
+ const runMinuteTick = () => {
61038
+ void fetchBootstrap(true);
61039
+ };
61040
+ const startInterval = () => {
61041
+ runMinuteTick();
61042
+ intervalId = window.setInterval(runMinuteTick, 6e4);
61043
+ };
61044
+ const msUntilNextMinute = (() => {
61045
+ const remainder = Date.now() % 6e4;
61046
+ return remainder === 0 ? 6e4 : 6e4 - remainder;
61047
+ })();
61048
+ timeoutId = window.setTimeout(startInterval, msUntilNextMinute);
61049
+ return () => {
61050
+ if (timeoutId !== null) {
61051
+ window.clearTimeout(timeoutId);
61052
+ }
61053
+ if (intervalId !== null) {
61054
+ window.clearInterval(intervalId);
61055
+ }
61056
+ };
61057
+ }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
61058
+ const isCurrentScopeResolved = state.requestKey === requestKey;
61059
+ return {
61060
+ resolvedScope: state.resolvedScope,
61061
+ scopeKey: state.scopeKey,
61062
+ lines: state.lines,
61063
+ workspaceMetrics: state.workspaceMetrics,
61064
+ lineMetrics: state.lineMetrics,
61065
+ kpiTrend: state.kpiTrend,
61066
+ activeBreaks: state.activeBreaks,
61067
+ videoStreamsByWorkspaceId: state.videoStreamsByWorkspaceId,
61068
+ efficiencyLegend: state.efficiencyLegend,
61069
+ metadata: state.metadata,
61070
+ isLoading: enabled ? isLoading || !isCurrentScopeResolved : false,
61071
+ isCurrentScopeResolved,
61072
+ error,
61073
+ refetch: () => fetchBootstrap(true)
61074
+ };
61075
+ };
61076
+
61077
+ // src/lib/utils/liveMonitorBootstrap.ts
61078
+ var toNumber3 = (value) => {
61079
+ if (typeof value === "number" && Number.isFinite(value)) return value;
61080
+ if (typeof value === "string" && value.trim() !== "") {
61081
+ const parsed = Number(value);
61082
+ return Number.isFinite(parsed) ? parsed : 0;
61083
+ }
61084
+ return 0;
61085
+ };
61086
+ var normalizeNullableNumber = (value) => {
61087
+ if (value === null || value === void 0 || value === "") return null;
61088
+ return toNumber3(value);
61089
+ };
61090
+ var getLiveMonitorBootstrapMode = () => {
61091
+ const rawMode = (process.env.NEXT_PUBLIC_MONITOR_BOOTSTRAP_MODE || "").trim().toLowerCase();
61092
+ if (rawMode === "legacy" || rawMode === "shadow" || rawMode === "bootstrap") {
61093
+ return rawMode;
61094
+ }
61095
+ return "bootstrap";
61096
+ };
61097
+ var normalizeMonitorShadowSnapshot = (input) => {
61098
+ const sortedSelectedLineIds = Array.from(new Set((input.selectedLineIds || []).filter(Boolean))).sort();
61099
+ const resolvedScope = (input.resolvedScope || []).filter((entry) => entry?.line_id && entry?.date && entry?.shift_id !== void 0 && entry?.shift_id !== null).map((entry) => ({
61100
+ line_id: entry.line_id,
61101
+ date: entry.date,
61102
+ shift_id: entry.shift_id
61103
+ })).sort((left, right) => {
61104
+ if (left.line_id !== right.line_id) return left.line_id.localeCompare(right.line_id);
61105
+ if (left.date !== right.date) return left.date.localeCompare(right.date);
61106
+ return left.shift_id - right.shift_id;
61107
+ });
61108
+ const lines = (input.lines || []).filter((entry) => entry?.line_id).map((entry) => ({
61109
+ line_id: entry.line_id,
61110
+ line_name: entry.line_name || "Unknown Line"
61111
+ })).sort((left, right) => left.line_id.localeCompare(right.line_id));
61112
+ const workspaces = (input.workspaces || []).map((workspace) => {
61113
+ const workspaceId = workspace.workspace_uuid || null;
61114
+ const stream = workspaceId ? input.videoStreamsByWorkspaceId?.[workspaceId] || null : null;
61115
+ return {
61116
+ workspace_id: workspaceId,
61117
+ line_id: workspace.line_id,
61118
+ workspace_name: workspace.workspace_name,
61119
+ display_name: workspace.displayName || null,
61120
+ efficiency: toNumber3(workspace.efficiency),
61121
+ trend: toNumber3(workspace.trend),
61122
+ show_exclamation: Boolean(workspace.show_exclamation),
61123
+ scheduled_break_active: Boolean(workspace.scheduled_break_active),
61124
+ action_count: toNumber3(workspace.action_count),
61125
+ action_threshold: toNumber3(workspace.action_threshold),
61126
+ predicted_output: toNumber3(workspace.predicted_output),
61127
+ avg_cycle_time: toNumber3(workspace.avg_cycle_time),
61128
+ pph: toNumber3(workspace.pph),
61129
+ recent_flow_percent: normalizeNullableNumber(workspace.recent_flow_percent),
61130
+ incoming_wip_current: normalizeNullableNumber(workspace.incoming_wip_current),
61131
+ video_stream: stream ? {
61132
+ workspace_id: stream.workspace_id,
61133
+ camera_uuid: stream.camera_uuid,
61134
+ stream_camera_uuid: stream.stream_camera_uuid,
61135
+ hls_url: stream.hls_url,
61136
+ crop: stream.crop
61137
+ } : null
61138
+ };
61139
+ }).sort((left, right) => {
61140
+ if (left.line_id !== right.line_id) return left.line_id.localeCompare(right.line_id);
61141
+ return left.workspace_name.localeCompare(right.workspace_name, void 0, { numeric: true });
61142
+ });
61143
+ const activeBreaks = (input.activeBreaks || []).map((activeBreak) => ({
61144
+ lineId: activeBreak.lineId,
61145
+ shiftName: activeBreak.shiftName,
61146
+ startTime: activeBreak.startTime,
61147
+ endTime: activeBreak.endTime,
61148
+ remainingMinutes: toNumber3(activeBreak.remainingMinutes)
61149
+ })).sort((left, right) => {
61150
+ if (left.lineId !== right.lineId) return left.lineId.localeCompare(right.lineId);
61151
+ if (left.shiftName !== right.shiftName) return left.shiftName.localeCompare(right.shiftName);
61152
+ return left.startTime.localeCompare(right.startTime);
61153
+ });
61154
+ return {
61155
+ selectedLineIds: sortedSelectedLineIds,
61156
+ resolvedScope,
61157
+ lines,
61158
+ workspaces,
61159
+ activeBreaks,
61160
+ kpis: input.kpis || null,
61161
+ kpiTrend: input.kpiTrend || null,
61162
+ efficiencyLegend: input.efficiencyLegend || null
61163
+ };
61164
+ };
61165
+ var diffMonitorShadowSnapshots = (legacySnapshot, bootstrapSnapshot) => {
61166
+ const mismatches = [];
61167
+ const compareSection = (section) => {
61168
+ const legacyValue = legacySnapshot[section];
61169
+ const bootstrapValue = bootstrapSnapshot[section];
61170
+ if (JSON.stringify(legacyValue) !== JSON.stringify(bootstrapValue)) {
61171
+ mismatches.push({
61172
+ section,
61173
+ legacy: legacyValue,
61174
+ bootstrap: bootstrapValue
61175
+ });
61176
+ }
61177
+ };
61178
+ compareSection("selectedLineIds");
61179
+ compareSection("resolvedScope");
61180
+ compareSection("lines");
61181
+ compareSection("workspaces");
61182
+ compareSection("activeBreaks");
61183
+ compareSection("kpis");
61184
+ compareSection("kpiTrend");
61185
+ compareSection("efficiencyLegend");
61186
+ return mismatches;
61187
+ };
60705
61188
 
60706
61189
  // src/lib/services/notificationService.ts
60707
61190
  var API_BASE_URL = process.env.NEXT_PUBLIC_BACKEND_URL;
@@ -60843,7 +61326,10 @@ var logDebug3 = (...args) => {
60843
61326
  if (!DEBUG_DASHBOARD_LOGS3) return;
60844
61327
  console.log(...args);
60845
61328
  };
61329
+ var EMPTY_LINE_IDS = [];
61330
+ var EMPTY_WORKSPACES = [];
60846
61331
  var LoadingPageCmp = LoadingPage_default;
61332
+ var LoadingOverlayCmp = LoadingOverlay_default;
60847
61333
  function HomeView({
60848
61334
  defaultLineId,
60849
61335
  factoryViewId,
@@ -60991,6 +61477,7 @@ function HomeView({
60991
61477
  return createNotificationService(supabaseClient);
60992
61478
  }, [supabaseClient]);
60993
61479
  const [bottleneckNotification, setBottleneckNotification] = useState(null);
61480
+ const showBottleneckNotificationRef = useRef(null);
60994
61481
  const [bottleneckModalOpen, setBottleneckModalOpen] = useState(false);
60995
61482
  const [bottleneckModalData, setBottleneckModalData] = useState(null);
60996
61483
  const [diagnosisModalOpen, setDiagnosisModalOpen] = useState(false);
@@ -61001,6 +61488,11 @@ function HomeView({
61001
61488
  dashboardConfig?.shiftConfig
61002
61489
  );
61003
61490
  const shouldEnableMetricsFetch = authStatus === "ready";
61491
+ const liveMonitorMode = useMemo(() => getLiveMonitorBootstrapMode(), []);
61492
+ const isLegacyMonitorMode = liveMonitorMode === "legacy";
61493
+ const isShadowMonitorMode = liveMonitorMode === "shadow";
61494
+ const isBootstrapMonitorMode = liveMonitorMode === "bootstrap";
61495
+ const lastShadowMismatchSignatureRef = useRef(null);
61004
61496
  const handleLineMetricsUpdate = useCallback(() => {
61005
61497
  if (trendRefreshTimerRef.current) {
61006
61498
  window.clearTimeout(trendRefreshTimerRef.current);
@@ -61016,45 +61508,24 @@ function HomeView({
61016
61508
  }
61017
61509
  }, []);
61018
61510
  const {
61019
- workspaceMetrics,
61020
- lineMetrics,
61021
- efficiencyLegend,
61022
- metadata: metricsMetadata,
61023
- isLoading: metricsLoading,
61024
- error: metricsError,
61025
- refetch: refetchMetrics
61511
+ workspaceMetrics: legacyWorkspaceMetrics,
61512
+ lineMetrics: legacyLineMetrics,
61513
+ efficiencyLegend: legacyEfficiencyLegend,
61514
+ metadata: legacyMetricsMetadata,
61515
+ isLoading: legacyMetricsLoading,
61516
+ isCurrentScopeResolved: legacyIsCurrentScopeResolved,
61517
+ error: legacyMetricsError,
61518
+ refetch: refetchLegacyMetrics
61026
61519
  } = useDashboardMetrics({
61027
61520
  lineId: metricsScopeLineId,
61028
61521
  lineIds: selectedLineIds,
61029
61522
  onLineMetricsUpdate: handleLineMetricsUpdate,
61030
61523
  userAccessibleLineIds: visibleLineIds,
61031
- // Pass user's accessible lines for supervisor filtering
61032
- enabled: shouldEnableMetricsFetch
61524
+ enabled: shouldEnableMetricsFetch && !isBootstrapMonitorMode
61033
61525
  });
61034
- const metricsDisplayNames = useMemo(() => {
61035
- const nextDisplayNames = {};
61036
- workspaceMetrics.forEach((workspace) => {
61037
- if (!workspace.displayName) {
61038
- return;
61039
- }
61040
- nextDisplayNames[`${workspace.line_id}_${workspace.workspace_name}`] = workspace.displayName;
61041
- });
61042
- return nextDisplayNames;
61043
- }, [workspaceMetrics]);
61044
- useEffect(() => {
61045
- workspaceMetrics.forEach((workspace) => {
61046
- if (!workspace.displayName) {
61047
- return;
61048
- }
61049
- upsertWorkspaceDisplayNameInCache({
61050
- lineId: workspace.line_id,
61051
- workspaceId: workspace.workspace_name,
61052
- displayName: workspace.displayName
61053
- });
61054
- });
61055
- }, [workspaceMetrics]);
61056
- const trendGroups = useMemo(() => {
61057
- const lineMetricsRows = lineMetrics || [];
61526
+ const legacyTrendGroups = useMemo(() => {
61527
+ if (isBootstrapMonitorMode) return null;
61528
+ const lineMetricsRows = legacyLineMetrics || [];
61058
61529
  if (!lineMetricsRows.length) return null;
61059
61530
  if (selectedLineIds.length > 1) {
61060
61531
  const rowsForLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
@@ -61080,7 +61551,7 @@ function HomeView({
61080
61551
  shiftId: group.shiftId
61081
61552
  }));
61082
61553
  }
61083
- const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
61554
+ const row = lineMetricsRows.find((candidate) => candidate?.line_id === primarySelectedLineId);
61084
61555
  if (!row?.date || row?.shift_id === void 0 || row?.shift_id === null) {
61085
61556
  return null;
61086
61557
  }
@@ -61089,69 +61560,119 @@ function HomeView({
61089
61560
  date: row.date,
61090
61561
  shiftId: row.shift_id
61091
61562
  }];
61092
- }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61093
- const trendOptions = useMemo(() => {
61094
- if (!trendGroups || !userCompanyId) return null;
61563
+ }, [isBootstrapMonitorMode, legacyLineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61564
+ const legacyTrendOptions = useMemo(() => {
61565
+ if (isBootstrapMonitorMode || !legacyTrendGroups || !userCompanyId) return null;
61095
61566
  return {
61096
- groups: trendGroups,
61567
+ groups: legacyTrendGroups,
61097
61568
  companyId: userCompanyId,
61098
61569
  refreshKey: trendRefreshKey,
61099
61570
  forceRefresh: trendRefreshKey > 0
61100
61571
  };
61101
- }, [trendGroups, userCompanyId, trendRefreshKey]);
61102
- const { trend: kpiTrend } = useKpiTrends(trendOptions);
61103
- const hasFlowBuffers = Boolean(metricsMetadata?.hasFlowBuffers);
61572
+ }, [isBootstrapMonitorMode, legacyTrendGroups, userCompanyId, trendRefreshKey]);
61573
+ const { trend: legacyKpiTrend } = useKpiTrends(legacyTrendOptions);
61574
+ const bootstrapMonitor = useLiveMonitorBootstrap({
61575
+ lineIds: selectedLineIds,
61576
+ companyId: userCompanyId,
61577
+ enabled: shouldEnableMetricsFetch && !isLegacyMonitorMode,
61578
+ appTimezone: timezone,
61579
+ workspaceConfig: dashboardConfig?.workspaceConfig,
61580
+ fallbackShiftConfig: dashboardConfig?.shiftConfig,
61581
+ lineShiftConfigs
61582
+ });
61583
+ const currentWorkspaceMetrics = isBootstrapMonitorMode ? bootstrapMonitor.workspaceMetrics : legacyWorkspaceMetrics;
61584
+ const currentLineMetrics = isBootstrapMonitorMode ? bootstrapMonitor.lineMetrics : legacyLineMetrics;
61585
+ const currentEfficiencyLegend = isBootstrapMonitorMode ? bootstrapMonitor.efficiencyLegend : legacyEfficiencyLegend;
61586
+ const currentMetricsMetadata = isBootstrapMonitorMode ? bootstrapMonitor.metadata : legacyMetricsMetadata;
61587
+ const currentMetricsLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyMetricsLoading;
61588
+ const currentIsCurrentScopeResolved = isBootstrapMonitorMode ? bootstrapMonitor.isCurrentScopeResolved : legacyIsCurrentScopeResolved;
61589
+ const currentMetricsError = isBootstrapMonitorMode ? bootstrapMonitor.error : legacyMetricsError;
61590
+ const currentRefetchMetrics = isBootstrapMonitorMode ? bootstrapMonitor.refetch : refetchLegacyMetrics;
61591
+ const metricsDisplayNames = useMemo(() => {
61592
+ const nextDisplayNames = {};
61593
+ currentWorkspaceMetrics.forEach((workspace) => {
61594
+ if (!workspace.displayName) {
61595
+ return;
61596
+ }
61597
+ nextDisplayNames[`${workspace.line_id}_${workspace.workspace_name}`] = workspace.displayName;
61598
+ });
61599
+ return nextDisplayNames;
61600
+ }, [currentWorkspaceMetrics]);
61104
61601
  useEffect(() => {
61105
- const sample = workspaceMetrics.slice(0, 3).map((workspace) => ({
61602
+ currentWorkspaceMetrics.forEach((workspace) => {
61603
+ if (!workspace.displayName) {
61604
+ return;
61605
+ }
61606
+ upsertWorkspaceDisplayNameInCache({
61607
+ lineId: workspace.line_id,
61608
+ workspaceId: workspace.workspace_name,
61609
+ displayName: workspace.displayName
61610
+ });
61611
+ });
61612
+ }, [currentWorkspaceMetrics]);
61613
+ const hasFlowBuffers = Boolean(currentMetricsMetadata?.hasFlowBuffers);
61614
+ useEffect(() => {
61615
+ const sample = currentWorkspaceMetrics.slice(0, 3).map((workspace) => ({
61106
61616
  id: workspace.workspace_uuid || workspace.workspace_name,
61107
61617
  efficiency: workspace.efficiency,
61108
61618
  trend: workspace.trend,
61109
61619
  lineId: workspace.line_id
61110
61620
  }));
61111
61621
  logDebug3("[HomeView] workspaceMetrics update:", {
61112
- count: workspaceMetrics.length,
61113
- isLoading: metricsLoading,
61114
- error: metricsError?.message,
61622
+ count: currentWorkspaceMetrics.length,
61623
+ isLoading: currentMetricsLoading,
61624
+ error: currentMetricsError?.message,
61115
61625
  sample
61116
61626
  });
61117
- }, [workspaceMetrics, metricsLoading, metricsError]);
61118
- const kpis = useMemo(() => {
61119
- const lineMetricsRows = lineMetrics || [];
61627
+ }, [currentWorkspaceMetrics, currentMetricsLoading, currentMetricsError]);
61628
+ const buildDisplayKpis = useCallback((sourceLineMetrics, sourceIsLoading, sourceKpiTrend) => {
61629
+ const lineMetricsRows = sourceLineMetrics || [];
61630
+ let baseKpis = null;
61120
61631
  if (selectedLineIds.length > 1) {
61121
- const rowsForSelectedLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
61122
- if (metricsLoading && rowsForSelectedLines.length === 0) return null;
61123
- return aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
61124
- }
61125
- const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
61126
- if (!row) {
61127
- if (metricsLoading) return null;
61128
- return buildKPIsFromLineMetricsRow(null);
61129
- }
61130
- return buildKPIsFromLineMetricsRow(row);
61131
- }, [lineMetrics, metricsLoading, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61132
- const kpisWithTrend = useMemo(() => {
61133
- if (!kpis) return null;
61134
- if (!kpiTrend) return kpis;
61632
+ const rowsForSelectedLines = lineMetricsRows.filter((row) => selectedLineIdSet.has(row?.line_id));
61633
+ if (sourceIsLoading && rowsForSelectedLines.length === 0) return null;
61634
+ baseKpis = aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
61635
+ } else {
61636
+ const row = lineMetricsRows.find((candidate) => candidate?.line_id === primarySelectedLineId);
61637
+ if (!row) {
61638
+ if (sourceIsLoading) return null;
61639
+ baseKpis = buildKPIsFromLineMetricsRow(null);
61640
+ } else {
61641
+ baseKpis = buildKPIsFromLineMetricsRow(row);
61642
+ }
61643
+ }
61644
+ if (!baseKpis || !sourceKpiTrend) {
61645
+ return baseKpis;
61646
+ }
61135
61647
  return {
61136
- ...kpis,
61648
+ ...baseKpis,
61137
61649
  efficiency: {
61138
- ...kpis.efficiency,
61139
- change: kpiTrend.efficiency?.delta_pp ?? kpis.efficiency.change
61650
+ ...baseKpis.efficiency,
61651
+ change: sourceKpiTrend.efficiency?.delta_pp ?? baseKpis.efficiency.change
61140
61652
  },
61141
61653
  outputProgress: {
61142
- ...kpis.outputProgress,
61143
- change: kpiTrend.outputProgress?.delta_pp ?? kpis.outputProgress.change
61654
+ ...baseKpis.outputProgress,
61655
+ change: sourceKpiTrend.outputProgress?.delta_pp ?? baseKpis.outputProgress.change
61144
61656
  },
61145
61657
  underperformingWorkers: {
61146
- ...kpis.underperformingWorkers,
61147
- change: kpiTrend.underperformingWorkers?.delta_count ?? kpis.underperformingWorkers.change
61658
+ ...baseKpis.underperformingWorkers,
61659
+ change: sourceKpiTrend.underperformingWorkers?.delta_count ?? baseKpis.underperformingWorkers.change
61148
61660
  },
61149
61661
  avgCycleTime: {
61150
- ...kpis.avgCycleTime,
61151
- change: kpiTrend.avgCycleTime?.delta_seconds ?? kpis.avgCycleTime.change
61662
+ ...baseKpis.avgCycleTime,
61663
+ change: sourceKpiTrend.avgCycleTime?.delta_seconds ?? baseKpis.avgCycleTime.change
61152
61664
  }
61153
61665
  };
61154
- }, [kpis, kpiTrend]);
61666
+ }, [primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61667
+ const legacyKpisWithTrend = useMemo(
61668
+ () => buildDisplayKpis(legacyLineMetrics, legacyMetricsLoading, legacyKpiTrend),
61669
+ [buildDisplayKpis, legacyLineMetrics, legacyMetricsLoading, legacyKpiTrend]
61670
+ );
61671
+ const bootstrapKpisWithTrend = useMemo(
61672
+ () => buildDisplayKpis(bootstrapMonitor.lineMetrics, bootstrapMonitor.isLoading, bootstrapMonitor.kpiTrend),
61673
+ [buildDisplayKpis, bootstrapMonitor.lineMetrics, bootstrapMonitor.isLoading, bootstrapMonitor.kpiTrend]
61674
+ );
61675
+ const currentKpisWithTrend = isBootstrapMonitorMode ? bootstrapKpisWithTrend : legacyKpisWithTrend;
61155
61676
  const selectedLineMeta = useMemo(
61156
61677
  () => selectedLineIds.length === 1 ? dbLines.find((line) => line.id === primarySelectedLineId) : void 0,
61157
61678
  [dbLines, primarySelectedLineId, selectedLineIds.length]
@@ -61160,40 +61681,121 @@ function HomeView({
61160
61681
  const isUptimeMode = selectedMonitoringMode === "uptime";
61161
61682
  const averageIdleTimeSeconds = useMemo(() => {
61162
61683
  if (!isUptimeMode) return null;
61163
- const targetWorkspaces = workspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
61684
+ const targetWorkspaces = currentWorkspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
61164
61685
  const idleValues = targetWorkspaces.map((ws) => ws.idle_time).filter((value) => Number.isFinite(value));
61165
61686
  if (idleValues.length === 0) return 0;
61166
61687
  const totalIdle = idleValues.reduce((sum, value) => sum + value, 0);
61167
61688
  return totalIdle / idleValues.length;
61168
- }, [isUptimeMode, primarySelectedLineId, workspaceMetrics]);
61689
+ }, [isUptimeMode, primarySelectedLineId, currentWorkspaceMetrics]);
61169
61690
  const {
61170
- activeBreaks: allActiveBreaks,
61171
- isLoading: breaksLoading,
61172
- error: breaksError
61173
- } = useActiveBreaks(visibleLineIds);
61174
- const activeBreaks = useMemo(() => {
61691
+ activeBreaks: legacyAllActiveBreaks,
61692
+ isLoading: legacyBreaksLoading,
61693
+ error: legacyBreaksError
61694
+ } = useActiveBreaks(isBootstrapMonitorMode ? EMPTY_LINE_IDS : visibleLineIds);
61695
+ const legacyActiveBreaks = useMemo(() => {
61175
61696
  if (isAllLinesSelection(selectedLineIds)) {
61176
- return allActiveBreaks;
61697
+ return legacyAllActiveBreaks;
61177
61698
  }
61178
- return allActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
61179
- }, [allActiveBreaks, selectedLineIdSet, selectedLineIds]);
61699
+ return legacyAllActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
61700
+ }, [legacyAllActiveBreaks, selectedLineIdSet, selectedLineIds]);
61701
+ const currentActiveBreaks = isBootstrapMonitorMode ? bootstrapMonitor.activeBreaks : legacyActiveBreaks;
61702
+ const currentBreaksLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyBreaksLoading;
61703
+ const currentBreaksError = isBootstrapMonitorMode ? bootstrapMonitor.error?.message || null : legacyBreaksError;
61180
61704
  const activeBreakLineIds = useMemo(
61181
- () => new Set(activeBreaks.map((breakItem) => breakItem.lineId)),
61182
- [activeBreaks]
61705
+ () => new Set(currentActiveBreaks.map((breakItem) => breakItem.lineId)),
61706
+ [currentActiveBreaks]
61183
61707
  );
61184
61708
  const workspaceMetricsWithBreakState = useMemo(
61185
- () => workspaceMetrics.map((workspace) => ({
61709
+ () => currentWorkspaceMetrics.map((workspace) => ({
61186
61710
  ...workspace,
61187
61711
  scheduled_break_active: activeBreakLineIds.has(workspace.line_id)
61188
61712
  })),
61189
- [workspaceMetrics, activeBreakLineIds]
61713
+ [currentWorkspaceMetrics, activeBreakLineIds]
61190
61714
  );
61191
61715
  const [breakNotificationsDismissed, setBreakNotificationsDismissed] = useState(false);
61192
61716
  useEffect(() => {
61193
- if (activeBreaks.length > 0) {
61717
+ if (currentActiveBreaks.length > 0) {
61194
61718
  setBreakNotificationsDismissed(false);
61195
61719
  }
61196
- }, [activeBreaks.length]);
61720
+ }, [currentActiveBreaks.length]);
61721
+ const {
61722
+ streamsByWorkspaceId: legacyVideoStreamsByWorkspaceId,
61723
+ isLoading: legacyVideoStreamsLoading
61724
+ } = useWorkspaceVideoStreams(isBootstrapMonitorMode ? EMPTY_WORKSPACES : legacyWorkspaceMetrics, {
61725
+ lineIds: isBootstrapMonitorMode ? EMPTY_LINE_IDS : selectedLineIds
61726
+ });
61727
+ const currentVideoStreamsByWorkspaceId = isBootstrapMonitorMode ? bootstrapMonitor.videoStreamsByWorkspaceId : legacyVideoStreamsByWorkspaceId;
61728
+ const currentVideoStreamsLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyVideoStreamsLoading;
61729
+ useEffect(() => {
61730
+ if (!isShadowMonitorMode) {
61731
+ lastShadowMismatchSignatureRef.current = null;
61732
+ return;
61733
+ }
61734
+ if (!shouldEnableMetricsFetch || legacyMetricsLoading || !legacyIsCurrentScopeResolved || bootstrapMonitor.isLoading || !bootstrapMonitor.isCurrentScopeResolved) {
61735
+ return;
61736
+ }
61737
+ const legacySnapshot = normalizeMonitorShadowSnapshot({
61738
+ selectedLineIds,
61739
+ workspaces: workspaceMetricsWithBreakState,
61740
+ kpis: legacyKpisWithTrend,
61741
+ kpiTrend: legacyKpiTrend,
61742
+ activeBreaks: legacyActiveBreaks,
61743
+ videoStreamsByWorkspaceId: legacyVideoStreamsByWorkspaceId,
61744
+ efficiencyLegend: legacyEfficiencyLegend
61745
+ });
61746
+ const bootstrapSnapshot = normalizeMonitorShadowSnapshot({
61747
+ selectedLineIds,
61748
+ resolvedScope: bootstrapMonitor.resolvedScope,
61749
+ lines: bootstrapMonitor.lines,
61750
+ workspaces: bootstrapMonitor.workspaceMetrics.map((workspace) => ({
61751
+ ...workspace,
61752
+ scheduled_break_active: bootstrapMonitor.activeBreaks.some((activeBreak) => activeBreak.lineId === workspace.line_id)
61753
+ })),
61754
+ kpis: bootstrapKpisWithTrend,
61755
+ kpiTrend: bootstrapMonitor.kpiTrend,
61756
+ activeBreaks: bootstrapMonitor.activeBreaks,
61757
+ videoStreamsByWorkspaceId: bootstrapMonitor.videoStreamsByWorkspaceId,
61758
+ efficiencyLegend: bootstrapMonitor.efficiencyLegend
61759
+ });
61760
+ const mismatches = diffMonitorShadowSnapshots(legacySnapshot, bootstrapSnapshot);
61761
+ const signature = JSON.stringify(mismatches);
61762
+ if (mismatches.length === 0) {
61763
+ lastShadowMismatchSignatureRef.current = null;
61764
+ return;
61765
+ }
61766
+ if (lastShadowMismatchSignatureRef.current === signature) {
61767
+ return;
61768
+ }
61769
+ lastShadowMismatchSignatureRef.current = signature;
61770
+ console.error("[HomeView][monitor-shadow-mismatch]", {
61771
+ selectedLineIds,
61772
+ mismatches,
61773
+ legacySnapshot,
61774
+ bootstrapSnapshot
61775
+ });
61776
+ }, [
61777
+ bootstrapKpisWithTrend,
61778
+ bootstrapMonitor.activeBreaks,
61779
+ bootstrapMonitor.efficiencyLegend,
61780
+ bootstrapMonitor.isCurrentScopeResolved,
61781
+ bootstrapMonitor.isLoading,
61782
+ bootstrapMonitor.kpiTrend,
61783
+ bootstrapMonitor.lines,
61784
+ bootstrapMonitor.resolvedScope,
61785
+ bootstrapMonitor.videoStreamsByWorkspaceId,
61786
+ bootstrapMonitor.workspaceMetrics,
61787
+ isShadowMonitorMode,
61788
+ legacyActiveBreaks,
61789
+ legacyEfficiencyLegend,
61790
+ legacyIsCurrentScopeResolved,
61791
+ legacyKpiTrend,
61792
+ legacyKpisWithTrend,
61793
+ legacyMetricsLoading,
61794
+ legacyVideoStreamsByWorkspaceId,
61795
+ selectedLineIds,
61796
+ shouldEnableMetricsFetch,
61797
+ workspaceMetricsWithBreakState
61798
+ ]);
61197
61799
  const showBottleneckNotification = useCallback(async (bottleneck) => {
61198
61800
  try {
61199
61801
  logDebug3("\u{1F514} [Notification] Raw bottleneck data:", bottleneck);
@@ -61371,6 +61973,9 @@ function HomeView({
61371
61973
  setBottleneckNotification(errorNotification);
61372
61974
  }
61373
61975
  }, [notificationService, timezone, dashboardConfig, lineShiftConfigs]);
61976
+ useEffect(() => {
61977
+ showBottleneckNotificationRef.current = showBottleneckNotification;
61978
+ }, [showBottleneckNotification]);
61374
61979
  useEffect(() => {
61375
61980
  const ticketsEnabled = dashboardConfig?.ticketsConfig?.enabled ?? true;
61376
61981
  if (!ticketsEnabled) {
@@ -61440,7 +62045,7 @@ function HomeView({
61440
62045
  });
61441
62046
  if (latestTicket.event_type === "bottleneck") {
61442
62047
  logDebug3("\u{1F514} [Polling] Showing bottleneck notification for:", latestTicket.log_number);
61443
- showBottleneckNotification(latestTicket);
62048
+ await showBottleneckNotificationRef.current?.(latestTicket);
61444
62049
  } else {
61445
62050
  logDebug3("\u26A0\uFE0F [Polling] Non-bottleneck ticket found but no handler configured:", latestTicket.event_type);
61446
62051
  }
@@ -61460,29 +62065,30 @@ function HomeView({
61460
62065
  clearInterval(pollInterval);
61461
62066
  clearTimeout(initialPollTimer);
61462
62067
  };
61463
- }, [notificationService, userCompanyId, user, showBottleneckNotification, dashboardConfig?.ticketsConfig?.enabled]);
62068
+ }, [
62069
+ dashboardConfig?.ticketsConfig?.enabled,
62070
+ notificationService,
62071
+ user?.email,
62072
+ user?.role_level,
62073
+ userCompanyId
62074
+ ]);
61464
62075
  const handleWorkspaceHover = useCallback((workspaceId) => {
61465
62076
  }, []);
61466
62077
  const handleWorkspaceHoverEnd = useCallback((workspaceId) => {
61467
62078
  }, []);
61468
- const {
61469
- streamsByWorkspaceId: videoStreamsByWorkspaceId,
61470
- isLoading: videoStreamsLoading
61471
- } = useWorkspaceVideoStreams(workspaceMetrics);
61472
- const memoizedKPIs = useMemo(() => kpisWithTrend, [
62079
+ const memoizedKPIs = useMemo(() => currentKpisWithTrend, [
61473
62080
  // Only update reference when values change by at least 1%
61474
- kpisWithTrend?.efficiency?.value ? Math.round(kpisWithTrend.efficiency.value) : null,
61475
- kpisWithTrend?.efficiency?.change,
61476
- kpisWithTrend?.underperformingWorkers?.current,
61477
- kpisWithTrend?.underperformingWorkers?.total,
61478
- kpisWithTrend?.underperformingWorkers?.change,
61479
- kpisWithTrend?.outputProgress?.current,
61480
- kpisWithTrend?.outputProgress?.target,
61481
- kpisWithTrend?.outputProgress?.change,
61482
- kpisWithTrend?.avgCycleTime?.value ? Math.round(kpisWithTrend.avgCycleTime.value * 10) / 10 : null,
61483
- // Round to 1 decimal
61484
- kpisWithTrend?.avgCycleTime?.change,
61485
- kpisWithTrend?.qualityCompliance?.value ? Math.round(kpisWithTrend.qualityCompliance.value) : null,
62081
+ currentKpisWithTrend?.efficiency?.value ? Math.round(currentKpisWithTrend.efficiency.value) : null,
62082
+ currentKpisWithTrend?.efficiency?.change,
62083
+ currentKpisWithTrend?.underperformingWorkers?.current,
62084
+ currentKpisWithTrend?.underperformingWorkers?.total,
62085
+ currentKpisWithTrend?.underperformingWorkers?.change,
62086
+ currentKpisWithTrend?.outputProgress?.current,
62087
+ currentKpisWithTrend?.outputProgress?.target,
62088
+ currentKpisWithTrend?.outputProgress?.change,
62089
+ currentKpisWithTrend?.avgCycleTime?.value ? Math.round(currentKpisWithTrend.avgCycleTime.value * 10) / 10 : null,
62090
+ currentKpisWithTrend?.avgCycleTime?.change,
62091
+ currentKpisWithTrend?.qualityCompliance?.value ? Math.round(currentKpisWithTrend.qualityCompliance.value) : null,
61486
62092
  selectedLineIdsKey
61487
62093
  ]);
61488
62094
  useEffect(() => {
@@ -61493,13 +62099,13 @@ function HomeView({
61493
62099
  dashboard_surface: "monitor"
61494
62100
  });
61495
62101
  }, []);
61496
- const metricsErrorMessage = metricsError?.message || null;
62102
+ const metricsErrorMessage = currentMetricsError?.message || null;
61497
62103
  const handleRetryDashboardData = useCallback(async () => {
61498
62104
  if (isRecoveringSession) {
61499
62105
  await retrySessionHydration();
61500
62106
  }
61501
- refetchMetrics();
61502
- }, [isRecoveringSession, refetchMetrics, retrySessionHydration]);
62107
+ await currentRefetchMetrics();
62108
+ }, [currentRefetchMetrics, isRecoveringSession, retrySessionHydration]);
61503
62109
  const getTrackedLineScope = useCallback((lineIdsForScope) => {
61504
62110
  if (isAllLinesSelection(lineIdsForScope)) {
61505
62111
  return factoryViewId;
@@ -61552,12 +62158,15 @@ function HomeView({
61552
62158
  updateSelectedLineIds(Array.from(currentSelection));
61553
62159
  }, [selectedLineIds, updateSelectedLineIds]);
61554
62160
  useEffect(() => {
61555
- if (!metricsLoading && isChangingFilter) {
62161
+ if (!isChangingFilter) {
62162
+ return;
62163
+ }
62164
+ if (!currentMetricsLoading && currentIsCurrentScopeResolved || currentMetricsError) {
61556
62165
  setIsChangingFilter(false);
61557
62166
  }
61558
- }, [metricsLoading, isChangingFilter]);
62167
+ }, [currentIsCurrentScopeResolved, currentMetricsError, currentMetricsLoading, isChangingFilter]);
61559
62168
  useEffect(() => {
61560
- if (shouldEnableMetricsFetch && !metricsLoading && !metricsError && !hasInitialDataLoaded) {
62169
+ if (shouldEnableMetricsFetch && !currentMetricsLoading && !currentMetricsError && !hasInitialDataLoaded) {
61561
62170
  setHasInitialDataLoaded(true);
61562
62171
  trackCoreEvent("monitor page loaded", {
61563
62172
  default_line_id: defaultLineId,
@@ -61566,7 +62175,7 @@ function HomeView({
61566
62175
  dashboard_surface: "monitor"
61567
62176
  });
61568
62177
  }
61569
- }, [shouldEnableMetricsFetch, metricsLoading, metricsError, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
62178
+ }, [shouldEnableMetricsFetch, currentMetricsLoading, currentMetricsError, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
61570
62179
  const lineTitle = useMemo(() => {
61571
62180
  return factoryName;
61572
62181
  }, [factoryName]);
@@ -61717,15 +62326,15 @@ function HomeView({
61717
62326
  return showLoading;
61718
62327
  };
61719
62328
  const isAuthBootstrapping = authStatus === "loading";
61720
- const isInitialLoading = !isHydrated || !hasInitialDataLoaded && (isAuthBootstrapping || shouldEnableMetricsFetch && metricsLoading);
61721
- const isDataLoading = metricsLoading;
62329
+ const isInitialLoading = !isHydrated || !hasInitialDataLoaded && (isAuthBootstrapping || shouldEnableMetricsFetch && currentMetricsLoading);
62330
+ const isDataLoading = currentMetricsLoading;
61722
62331
  const hasKpiDataReady = useMemo(() => {
61723
- const lineMetricsRows = lineMetrics || [];
62332
+ const lineMetricsRows = currentLineMetrics || [];
61724
62333
  if (selectedLineIds.length > 1) {
61725
62334
  return lineMetricsRows.some((row) => selectedLineIdSet.has(row?.line_id));
61726
62335
  }
61727
62336
  return lineMetricsRows.some((row) => row?.line_id === primarySelectedLineId);
61728
- }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
62337
+ }, [currentLineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61729
62338
  const isKpiLoading = !hasKpiDataReady;
61730
62339
  const shouldShowReconnectScreen = !hasInitialDataLoaded && (isRecoveringSession || !shouldEnableMetricsFetch && authStatus !== "failed" || !!metricsErrorMessage && authStatus !== "failed");
61731
62340
  const shouldShowFatalLoadFailure = !hasInitialDataLoaded && !!metricsErrorMessage && authStatus === "failed";
@@ -61753,6 +62362,7 @@ function HomeView({
61753
62362
  return () => clearTimeout(timer);
61754
62363
  }, [isDataLoading]);
61755
62364
  const shouldShowDataLoading = showDataLoading || isDataLoading;
62365
+ const shouldShowFilterChangeLoader = isChangingFilter && !currentIsCurrentScopeResolved;
61756
62366
  const shouldShowKpiLoading = useSmoothLoading(isKpiLoading, 400);
61757
62367
  const displayKpis = shouldShowKpiLoading ? null : memoizedKPIs;
61758
62368
  const kpiSectionControl = useMemo(() => /* @__PURE__ */ jsx(
@@ -61837,7 +62447,7 @@ function HomeView({
61837
62447
  }
61838
62448
  )
61839
62449
  ] }) }) : null,
61840
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsx(
62450
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsx("div", { className: "relative flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsx(
61841
62451
  motion.div,
61842
62452
  {
61843
62453
  initial: { opacity: 0, scale: 0.98 },
@@ -61849,10 +62459,10 @@ function HomeView({
61849
62459
  lineNames: mergedLineNames,
61850
62460
  lineOrder: selectedLineIds,
61851
62461
  factoryView: factoryViewId,
61852
- legend: efficiencyLegend,
62462
+ legend: currentEfficiencyLegend,
61853
62463
  videoSources,
61854
- videoStreamsByWorkspaceId,
61855
- videoStreamsLoading,
62464
+ videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
62465
+ videoStreamsLoading: currentVideoStreamsLoading,
61856
62466
  displayNames: metricsDisplayNames,
61857
62467
  hasFlowBuffers,
61858
62468
  className: "h-full",
@@ -61860,8 +62470,7 @@ function HomeView({
61860
62470
  onWorkspaceHover: handleWorkspaceHover,
61861
62471
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
61862
62472
  })
61863
- },
61864
- selectedLineIdsKey
62473
+ }
61865
62474
  ) : !shouldShowDataLoading && hasInitialDataLoaded ? /* @__PURE__ */ jsx(
61866
62475
  motion.div,
61867
62476
  {
@@ -61883,10 +62492,10 @@ function HomeView({
61883
62492
  lineNames: mergedLineNames,
61884
62493
  lineOrder: selectedLineIds,
61885
62494
  factoryView: factoryViewId,
61886
- legend: efficiencyLegend,
62495
+ legend: currentEfficiencyLegend,
61887
62496
  videoSources,
61888
- videoStreamsByWorkspaceId,
61889
- videoStreamsLoading,
62497
+ videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
62498
+ videoStreamsLoading: currentVideoStreamsLoading,
61890
62499
  displayNames: metricsDisplayNames,
61891
62500
  hasFlowBuffers,
61892
62501
  className: "h-full",
@@ -61894,16 +62503,15 @@ function HomeView({
61894
62503
  onWorkspaceHover: handleWorkspaceHover,
61895
62504
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
61896
62505
  })
61897
- },
61898
- selectedLineIdsKey
62506
+ }
61899
62507
  ) }) })
61900
62508
  ] }),
61901
62509
  /* @__PURE__ */ jsx(
61902
62510
  BreakNotificationPopup,
61903
62511
  {
61904
- activeBreaks,
62512
+ activeBreaks: currentActiveBreaks,
61905
62513
  lineNames: mergedLineNames,
61906
- isVisible: !breaksLoading && !breaksError && !breakNotificationsDismissed,
62514
+ isVisible: !currentBreaksLoading && !currentBreaksError && !breakNotificationsDismissed,
61907
62515
  onDismiss: () => setBreakNotificationsDismissed(true)
61908
62516
  }
61909
62517
  ),
@@ -61915,6 +62523,15 @@ function HomeView({
61915
62523
  onDismiss: () => setBottleneckNotification(null)
61916
62524
  }
61917
62525
  ),
62526
+ /* @__PURE__ */ jsx(
62527
+ LoadingOverlayCmp,
62528
+ {
62529
+ isVisible: shouldShowFilterChangeLoader,
62530
+ message: "Loading Dashboard...",
62531
+ className: "bg-slate-50/30 backdrop-blur-[3px]",
62532
+ contentVariant: "plain"
62533
+ }
62534
+ ),
61918
62535
  diagnosisModalOpen && bottleneckModalData?.clipId && /* @__PURE__ */ jsx(
61919
62536
  DiagnosisVideoModal,
61920
62537
  {
@@ -71471,6 +72088,8 @@ var WorkspaceDetailView = ({
71471
72088
  const totalActions = Number(cachedOverviewMetrics.action_count || 0);
71472
72089
  const targetOutput = Number(cachedOverviewMetrics.action_threshold || 0);
71473
72090
  const idealOutput = Number(cachedOverviewMetrics.predicted_output ?? targetOutput ?? 0);
72091
+ const pphThreshold = Number(cachedOverviewMetrics.pph_threshold || 0);
72092
+ const idealCycleTime = Number(cachedOverviewMetrics.ideal_cycle_time || 0);
71474
72093
  return {
71475
72094
  line_id: cachedOverviewMetrics.line_id,
71476
72095
  line_name: "",
@@ -71489,11 +72108,11 @@ var WorkspaceDetailView = ({
71489
72108
  shift_start: shiftDefinition?.startTime || "",
71490
72109
  shift_end: shiftDefinition?.endTime || "",
71491
72110
  shift_type: shiftType,
71492
- pph_threshold: 0,
72111
+ pph_threshold: pphThreshold,
71493
72112
  target_output: targetOutput,
71494
72113
  avg_pph: Number(cachedOverviewMetrics.pph || 0),
71495
72114
  avg_cycle_time: avgCycleTime,
71496
- ideal_cycle_time: avgCycleTime,
72115
+ ideal_cycle_time: idealCycleTime,
71497
72116
  avg_efficiency: Number(cachedOverviewMetrics.efficiency || 0),
71498
72117
  total_actions: totalActions,
71499
72118
  hourly_action_counts: [],
@@ -74025,7 +74644,8 @@ var TeamManagementView = ({
74025
74644
  const bootstrapData = await bootstrapResponse.json();
74026
74645
  setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
74027
74646
  id: factory.id,
74028
- factory_name: factory.factory_name
74647
+ factory_name: factory.factory_name,
74648
+ company_id: factory.company_id
74029
74649
  })));
74030
74650
  setAvailableLines(bootstrapData.lines || []);
74031
74651
  setUsers(bootstrapData.users || []);
@@ -78993,7 +79613,7 @@ var efficiencyLineConfig = [
78993
79613
  var bumpRenderCounter = (key) => {
78994
79614
  if (process.env.NODE_ENV === "test") ;
78995
79615
  };
78996
- var toNumber3 = (value) => {
79616
+ var toNumber4 = (value) => {
78997
79617
  if (typeof value === "number" && Number.isFinite(value)) return value;
78998
79618
  if (typeof value === "string" && value.trim().length > 0) {
78999
79619
  const parsed = Number(value);
@@ -79528,7 +80148,7 @@ var OverviewSummaryCards = React143__default.memo(({ store }) => {
79528
80148
  workspaceId: item.workspace_id || "",
79529
80149
  workspaceName: item.workspace_name?.trim() || item.workspace_id || "Unknown",
79530
80150
  lineName: item.line_name?.trim() || "Unknown Line",
79531
- avgIdleSeconds: toNumber3(item.avg_idle_seconds)
80151
+ avgIdleSeconds: toNumber4(item.avg_idle_seconds)
79532
80152
  })).slice(0, 5);
79533
80153
  }, [snapshot.data.summary.avg_idle_per_workstation?.top_contributors]);
79534
80154
  const showIdleContributorLineNames = React143__default.useMemo(() => {
@@ -79743,9 +80363,9 @@ var PoorestPerformersCard = React143__default.memo(({
79743
80363
  return {
79744
80364
  id: lineId,
79745
80365
  name: line.line_name?.trim() || "Unknown Line",
79746
- efficiency: roundOne(toNumber3(line.avg_efficiency) || 0),
79747
- previousEfficiency: toNumber3(line.previous_avg_efficiency),
79748
- delta: toNumber3(line.delta_pp),
80366
+ efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
80367
+ previousEfficiency: toNumber4(line.previous_avg_efficiency),
80368
+ delta: toNumber4(line.delta_pp),
79749
80369
  supervisor: supervisor?.displayName || "Unassigned",
79750
80370
  supervisorImage: supervisor?.profilePhotoUrl ?? null
79751
80371
  };
@@ -79854,14 +80474,14 @@ var IdleBreakdownCard = React143__default.memo(({
79854
80474
  paletteToken: item.palette_token?.trim(),
79855
80475
  iconToken: item.icon_token?.trim(),
79856
80476
  isKnown: item.is_known ?? void 0,
79857
- value: toNumber3(item.percentage) || 0,
79858
- totalDurationSeconds: toNumber3(item.total_duration_seconds),
79859
- efficiencyLossPercentage: toNumber3(item.efficiency_loss_percentage),
80477
+ value: toNumber4(item.percentage) || 0,
80478
+ totalDurationSeconds: toNumber4(item.total_duration_seconds),
80479
+ efficiencyLossPercentage: toNumber4(item.efficiency_loss_percentage),
79860
80480
  contributors: (item.contributors || []).map((contributor) => ({
79861
80481
  workspaceId: contributor.workspace_id || "",
79862
80482
  workspaceName: contributor.workspace_name?.trim() || "Unknown",
79863
- totalDurationSeconds: toNumber3(contributor.total_duration_seconds),
79864
- percentageWithinReason: toNumber3(contributor.percentage_within_reason)
80483
+ totalDurationSeconds: toNumber4(contributor.total_duration_seconds),
80484
+ percentageWithinReason: toNumber4(contributor.percentage_within_reason)
79865
80485
  }))
79866
80486
  })).filter((item) => item.value > 0);
79867
80487
  }, [idle.data]);
@@ -79930,7 +80550,7 @@ var EfficiencyTrendCard = React143__default.memo(({
79930
80550
  return `${hour12}:${minutes.toString().padStart(2, "0")} ${suffix}`;
79931
80551
  })(),
79932
80552
  efficiency: (() => {
79933
- const value = toNumber3(point.avg_efficiency);
80553
+ const value = toNumber4(point.avg_efficiency);
79934
80554
  return value === null ? void 0 : value;
79935
80555
  })()
79936
80556
  }));
@@ -79943,7 +80563,7 @@ var EfficiencyTrendCard = React143__default.memo(({
79943
80563
  name: format(pointDate, "MMM d"),
79944
80564
  dayOfWeek: format(pointDate, "EEEE"),
79945
80565
  efficiency: (() => {
79946
- const value = toNumber3(point.avg_efficiency);
80566
+ const value = toNumber4(point.avg_efficiency);
79947
80567
  return value === null ? void 0 : value;
79948
80568
  })()
79949
80569
  }]];
@@ -79967,7 +80587,7 @@ var EfficiencyTrendCard = React143__default.memo(({
79967
80587
  name: pointDate ? format(pointDate, "MMM d") : "",
79968
80588
  dayOfWeek: pointDate ? format(pointDate, "EEEE") : "",
79969
80589
  efficiency: (() => {
79970
- const value = toNumber3(point.avg_efficiency);
80590
+ const value = toNumber4(point.avg_efficiency);
79971
80591
  return value === null ? void 0 : value;
79972
80592
  })()
79973
80593
  };
@@ -80091,7 +80711,7 @@ var debugRefreshLog = (message, payload) => {
80091
80711
  var isAbortError2 = (error) => {
80092
80712
  return error instanceof DOMException && error.name === "AbortError";
80093
80713
  };
80094
- var toNumber4 = (value) => {
80714
+ var toNumber5 = (value) => {
80095
80715
  if (typeof value === "number" && Number.isFinite(value)) return value;
80096
80716
  if (typeof value === "string" && value.trim().length > 0) {
80097
80717
  const parsed = Number(value);
@@ -80100,11 +80720,11 @@ var toNumber4 = (value) => {
80100
80720
  return null;
80101
80721
  };
80102
80722
  var formatImprovementMetric = (recommendation) => {
80103
- const estimatedGain = toNumber4(recommendation.estimated_gain_pieces);
80723
+ const estimatedGain = toNumber5(recommendation.estimated_gain_pieces);
80104
80724
  if (estimatedGain !== null && estimatedGain > 0) {
80105
80725
  return `+${Math.round(estimatedGain).toLocaleString()} pcs / day`;
80106
80726
  }
80107
- const idleMinutes = toNumber4(recommendation.metrics?.idle_minutes);
80727
+ const idleMinutes = toNumber5(recommendation.metrics?.idle_minutes);
80108
80728
  if (idleMinutes !== null) {
80109
80729
  return `-${Math.round(idleMinutes)}m Idle`;
80110
80730
  }