@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.js CHANGED
@@ -5117,6 +5117,27 @@ var workspaceService = {
5117
5117
  this._workspaceVideoStreamsInFlight.set(cacheKey, fetchPromise);
5118
5118
  return fetchPromise;
5119
5119
  },
5120
+ primeWorkspaceVideoStreamsCache(params) {
5121
+ const streams = params.streams || {};
5122
+ const workspaceIds = (params.workspaceIds || []).filter(Boolean);
5123
+ const lineIds = (params.lineIds || []).filter(Boolean);
5124
+ if (!Object.keys(streams).length || !workspaceIds.length && !lineIds.length) {
5125
+ return;
5126
+ }
5127
+ const timestamp = Date.now();
5128
+ const entry = {
5129
+ streams,
5130
+ timestamp
5131
+ };
5132
+ if (workspaceIds.length) {
5133
+ const workspaceKey = workspaceIds.slice().sort().join(",");
5134
+ this._workspaceVideoStreamsCache.set(`workspaces:${workspaceKey}`, entry);
5135
+ }
5136
+ if (lineIds.length) {
5137
+ const lineKey = lineIds.slice().sort().join(",");
5138
+ this._workspaceVideoStreamsCache.set(`lines:${lineKey}`, entry);
5139
+ }
5140
+ },
5120
5141
  async getWorkspaceCameraIps(params) {
5121
5142
  const workspaceIds = (params.workspaceIds || []).filter(Boolean);
5122
5143
  const lineIds = (params.lineIds || []).filter(Boolean);
@@ -11988,8 +12009,8 @@ var useOperationalShiftKey = ({
11988
12009
  };
11989
12010
 
11990
12011
  // src/lib/stores/workspaceMetricsStore.ts
11991
- var OVERVIEW_TTL_MS = 3e4;
11992
- var DETAILED_TTL_MS = 3e4;
12012
+ var OVERVIEW_TTL_MS = 12e4;
12013
+ var DETAILED_TTL_MS = 12e4;
11993
12014
  var overviewByKey = /* @__PURE__ */ new Map();
11994
12015
  var detailedByKey = /* @__PURE__ */ new Map();
11995
12016
  var latestOverviewByWorkspace = /* @__PURE__ */ new Map();
@@ -13485,12 +13506,124 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
13485
13506
  }
13486
13507
  }
13487
13508
 
13509
+ // src/lib/utils/monitorWorkspaceMetrics.ts
13510
+ var sortWorkspaceMetrics = (left, right) => {
13511
+ if (left.line_id !== right.line_id) {
13512
+ return left.line_id.localeCompare(right.line_id);
13513
+ }
13514
+ return left.workspace_name.localeCompare(right.workspace_name, void 0, { numeric: true });
13515
+ };
13516
+ var buildLineMetricsById = (lineMetrics) => (lineMetrics || []).reduce((acc, lineMetric) => {
13517
+ const lineId = lineMetric?.line_id;
13518
+ if (!lineId) {
13519
+ return acc;
13520
+ }
13521
+ acc[lineId] = { total_workspaces: lineMetric?.total_workspaces };
13522
+ return acc;
13523
+ }, {});
13524
+ var transformMonitorWorkspaceMetrics = ({
13525
+ rows,
13526
+ companyId,
13527
+ workspaceConfig,
13528
+ appTimezone,
13529
+ lineMetrics,
13530
+ resolveShiftConfig,
13531
+ shouldOverrideShiftType,
13532
+ fallbackShiftConfig = null
13533
+ }) => {
13534
+ const effectiveWorkspaceConfig = workspaceConfig || DEFAULT_WORKSPACE_CONFIG;
13535
+ const detailTimezone = appTimezone || "Asia/Kolkata";
13536
+ const lineMetricsById = buildLineMetricsById(lineMetrics);
13537
+ return (rows || []).map((item) => {
13538
+ const lineId = String(item?.line_id || "");
13539
+ const lineShiftConfig = resolveShiftConfig?.(lineId) || fallbackShiftConfig || void 0;
13540
+ const detailedMetrics = toWorkspaceDetailedMetrics({
13541
+ data: item,
13542
+ companyId,
13543
+ workspaceConfig: effectiveWorkspaceConfig,
13544
+ shiftConfig: lineShiftConfig,
13545
+ appTimezone: detailTimezone,
13546
+ lineMetricsById,
13547
+ overrideShiftType: shouldOverrideShiftType?.(lineId) ?? false
13548
+ });
13549
+ if (detailedMetrics) {
13550
+ workspaceMetricsStore.setDetailed(detailedMetrics);
13551
+ }
13552
+ const idleTimeValue = typeof item.idle_time === "number" ? item.idle_time : Number(item.idle_time);
13553
+ const idleTimeSeconds = Number.isFinite(idleTimeValue) ? idleTimeValue : void 0;
13554
+ const pphThresholdValue = typeof item.pph_threshold === "number" ? item.pph_threshold : Number(item.pph_threshold);
13555
+ const actionFamily = normalizeActionFamily({
13556
+ actionFamily: item.action_family,
13557
+ actionType: item.action_type,
13558
+ actionName: item.action_name
13559
+ });
13560
+ const actionType = actionFamily === "assembly" || actionFamily === "output" ? actionFamily : null;
13561
+ const metric = {
13562
+ company_id: item.company_id || companyId,
13563
+ line_id: lineId,
13564
+ shift_id: item.shift_id,
13565
+ date: item.date,
13566
+ workspace_uuid: item.workspace_id,
13567
+ workspace_name: item.workspace_name,
13568
+ displayName: item.workspace_display_name || item.display_name || void 0,
13569
+ action_count: item.total_output || 0,
13570
+ pph: item.avg_pph || 0,
13571
+ pph_threshold: Number.isFinite(pphThresholdValue) ? pphThresholdValue : void 0,
13572
+ performance_score: item.performance_score || 0,
13573
+ avg_cycle_time: item.avg_cycle_time || 0,
13574
+ ideal_cycle_time: item.ideal_cycle_time || void 0,
13575
+ trend: item.trend_score === 1 ? 2 : 0,
13576
+ predicted_output: item.ideal_output || 0,
13577
+ efficiency: item.efficiency || 0,
13578
+ action_threshold: item.total_day_output || 0,
13579
+ monitoring_mode: item.monitoring_mode ?? void 0,
13580
+ idle_time: idleTimeSeconds,
13581
+ idle_time_hourly: item.idle_time_hourly ?? null,
13582
+ shift_start: item.shift_start ?? void 0,
13583
+ shift_end: item.shift_end ?? void 0,
13584
+ assembly_enabled: item.assembly_enabled ?? false,
13585
+ video_grid_metric_mode: normalizeVideoGridMetricMode(
13586
+ item.video_grid_metric_mode,
13587
+ item.assembly_enabled ?? false
13588
+ ),
13589
+ action_type: actionType,
13590
+ action_family: actionFamily,
13591
+ action_display_name: getActionDisplayName({
13592
+ displayName: item.action_display_name,
13593
+ actionFamily: item.action_family,
13594
+ actionType: item.action_type,
13595
+ actionName: item.action_name
13596
+ }),
13597
+ recent_flow_percent: item.recent_flow_percent ?? null,
13598
+ recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
13599
+ recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
13600
+ recent_flow_computed_at: item.recent_flow_computed_at ?? null,
13601
+ scheduled_break_active: item.scheduled_break_active ?? false,
13602
+ incoming_wip_current: item.incoming_wip_current ?? null,
13603
+ incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
13604
+ incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null,
13605
+ show_exclamation: item.show_exclamation ?? void 0
13606
+ };
13607
+ workspaceMetricsStore.setOverview(metric);
13608
+ return metric;
13609
+ }).sort(sortWorkspaceMetrics);
13610
+ };
13611
+
13488
13612
  // src/lib/hooks/useDashboardMetrics.ts
13489
13613
  var DEBUG_DASHBOARD_LOGS = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
13614
+ var REALTIME_REFRESH_DEBOUNCE_MS = 1500;
13615
+ var REALTIME_REFRESH_MIN_INTERVAL_MS = 5e3;
13490
13616
  var logDebug = (...args) => {
13491
13617
  if (!DEBUG_DASHBOARD_LOGS) return;
13492
13618
  console.log(...args);
13493
13619
  };
13620
+ var buildMetricsScopeKey = (lineId, lineIds) => {
13621
+ const normalizedLineIds = Array.from(new Set((lineIds || []).filter(Boolean))).sort();
13622
+ if (normalizedLineIds.length > 0) {
13623
+ return `${lineId}|${normalizedLineIds.join(",")}`;
13624
+ }
13625
+ return lineId;
13626
+ };
13494
13627
  var parseEfficiencyLegend = (legend) => {
13495
13628
  if (!legend) return null;
13496
13629
  const coerce = (value, fallback) => {
@@ -13570,6 +13703,7 @@ var useDashboardMetrics = ({
13570
13703
  const supabase = useSupabase();
13571
13704
  const [metrics2, setMetrics] = React143.useState({ workspaceMetrics: [], lineMetrics: [] });
13572
13705
  const [metricsLineId, setMetricsLineId] = React143.useState(lineId ?? null);
13706
+ const [metricsScopeKey, setMetricsScopeKey] = React143.useState(() => buildMetricsScopeKey(lineId, lineIds));
13573
13707
  const [isLoading, setIsLoading] = React143.useState(true);
13574
13708
  const [error, setError] = React143.useState(null);
13575
13709
  const lineIdRef = React143.useRef(lineId);
@@ -13579,8 +13713,10 @@ var useDashboardMetrics = ({
13579
13713
  const abortControllerRef = React143.useRef(null);
13580
13714
  const lastFetchKeyRef = React143.useRef(null);
13581
13715
  const inFlightFetchKeyRef = React143.useRef(null);
13582
- const updateQueueRef = React143.useRef(false);
13583
13716
  const onLineMetricsUpdateRef = React143.useRef(onLineMetricsUpdate);
13717
+ const pendingRealtimeRefreshRef = React143.useRef(false);
13718
+ const realtimeRefreshTimerRef = React143.useRef(null);
13719
+ const lastRealtimeRefreshStartedAtRef = React143.useRef(0);
13584
13720
  const shiftGroupsRef = React143.useRef(shiftGroups);
13585
13721
  const operationalShiftKeyRef = React143.useRef(operationalShiftKey);
13586
13722
  const configuredLineIdsRef = React143.useRef(configuredLineIds);
@@ -13608,15 +13744,17 @@ var useDashboardMetrics = ({
13608
13744
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
13609
13745
  [entityConfig.companyId]
13610
13746
  );
13747
+ const requestedScopeKey = React143.useMemo(
13748
+ () => buildMetricsScopeKey(lineId, isFactoryView ? targetFactoryLineIds : void 0),
13749
+ [isFactoryView, lineId, targetFactoryLineIds]
13750
+ );
13611
13751
  React143.useEffect(() => {
13612
13752
  lineIdRef.current = lineId;
13613
- setMetrics({ workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND });
13614
- setMetricsLineId(null);
13615
13753
  setIsLoading(true);
13616
13754
  setError(null);
13617
13755
  lastFetchKeyRef.current = null;
13618
13756
  inFlightFetchKeyRef.current = null;
13619
- }, [lineId]);
13757
+ }, [lineId, requestedScopeKey]);
13620
13758
  const fetchAllMetrics = React143.useCallback(async (options = {}) => {
13621
13759
  const { force = false } = options;
13622
13760
  const currentLineIdToUse = lineIdRef.current;
@@ -13629,7 +13767,6 @@ var useDashboardMetrics = ({
13629
13767
  companySpecificMetricsTable
13630
13768
  });
13631
13769
  if (!currentLineIdToUse || !supabase || !enabled || shiftLoading || isTimezoneLoading || companySpecificMetricsTable.includes("unknown_company")) {
13632
- updateQueueRef.current = false;
13633
13770
  if (!metrics2?.workspaceMetrics?.length && !metrics2?.lineMetrics?.length && !shiftLoading) setIsLoading(false);
13634
13771
  if (companySpecificMetricsTable.includes("unknown_company") && !error) {
13635
13772
  setError({ message: "Company ID not configured for metrics table.", code: "CONFIG_ERROR" });
@@ -13642,6 +13779,10 @@ var useDashboardMetrics = ({
13642
13779
  const usesShiftGroups = isFactory && shiftGroups.length > 0;
13643
13780
  const singleShiftDetails = usesShiftGroups ? null : shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : shiftGroups.length === 1 ? { date: shiftGroups[0].date, shiftId: shiftGroups[0].shiftId } : getCurrentShift(defaultTimezone, staticShiftConfig);
13644
13781
  const fetchKey = usesShiftGroups ? `factory|${companyId || "unknown"}|${shiftGroupsKey}` : isFactory ? `factory|${companyId || "unknown"}|${singleShiftDetails?.date}|${singleShiftDetails?.shiftId}|${targetLineIdsKey}` : `${currentLineIdToUse}|${companyId || "unknown"}|${singleShiftDetails?.date}|${singleShiftDetails?.shiftId}`;
13782
+ const responseScopeKey = buildMetricsScopeKey(
13783
+ currentLineIdToUse,
13784
+ isFactory ? targetLineIds : void 0
13785
+ );
13645
13786
  logDebug("[useDashboardMetrics] Fetch key details:", {
13646
13787
  isFactory,
13647
13788
  usesShiftGroups,
@@ -13651,14 +13792,8 @@ var useDashboardMetrics = ({
13651
13792
  singleShiftDetails,
13652
13793
  fetchKey
13653
13794
  });
13654
- if (inFlightFetchKeyRef.current === fetchKey) {
13655
- updateQueueRef.current = false;
13656
- return;
13657
- }
13658
- if (!force && lastFetchKeyRef.current === fetchKey) {
13659
- updateQueueRef.current = false;
13660
- return;
13661
- }
13795
+ if (inFlightFetchKeyRef.current === fetchKey) return;
13796
+ if (!force && lastFetchKeyRef.current === fetchKey) return;
13662
13797
  const requestId = ++activeRequestIdRef.current;
13663
13798
  const requestLineId = currentLineIdToUse;
13664
13799
  isFetchingRef.current = true;
@@ -13681,6 +13816,7 @@ var useDashboardMetrics = ({
13681
13816
  efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND
13682
13817
  });
13683
13818
  setMetricsLineId(requestLineId);
13819
+ setMetricsScopeKey(responseScopeKey);
13684
13820
  lastFetchKeyRef.current = inFlightFetchKeyRef.current;
13685
13821
  return;
13686
13822
  }
@@ -13790,89 +13926,16 @@ var useDashboardMetrics = ({
13790
13926
  }
13791
13927
  efficiencyLegend = parseEfficiencyLegend(backendData?.efficiency_legend) ?? DEFAULT_EFFICIENCY_LEGEND;
13792
13928
  }
13793
- const lineMetricsById = (allLineMetrics || []).reduce(
13794
- (acc, line) => {
13795
- const lineId2 = line?.line_id;
13796
- if (lineId2) {
13797
- acc[lineId2] = { total_workspaces: line?.total_workspaces };
13798
- }
13799
- return acc;
13800
- },
13801
- {}
13802
- );
13803
13929
  const detailTimezone = appTimezone || "Asia/Kolkata";
13804
- allWorkspaceMetrics.forEach((item) => {
13805
- const lineShiftConfig = isFactoryView ? multiLineShiftConfigMap.get(item.line_id) : shiftConfig;
13806
- const detailedMetrics = toWorkspaceDetailedMetrics({
13807
- data: item,
13808
- companyId: companyId || "",
13809
- workspaceConfig: effectiveWorkspaceConfig,
13810
- shiftConfig: lineShiftConfig || staticShiftConfig,
13811
- appTimezone: detailTimezone,
13812
- lineMetricsById,
13813
- overrideShiftType: Boolean(lineShiftConfig)
13814
- });
13815
- if (detailedMetrics) {
13816
- workspaceMetricsStore.setDetailed(detailedMetrics);
13817
- }
13818
- });
13819
- const transformedWorkspaceData = allWorkspaceMetrics.map((item) => {
13820
- const idleTimeValue = typeof item.idle_time === "number" ? item.idle_time : Number(item.idle_time);
13821
- const idleTimeSeconds = Number.isFinite(idleTimeValue) ? idleTimeValue : void 0;
13822
- const actionFamily = normalizeActionFamily({
13823
- actionFamily: item.action_family,
13824
- actionType: item.action_type,
13825
- actionName: item.action_name
13826
- });
13827
- const actionType = actionFamily === "assembly" || actionFamily === "output" ? actionFamily : null;
13828
- return {
13829
- company_id: item.company_id || companyId,
13830
- line_id: item.line_id,
13831
- shift_id: item.shift_id,
13832
- date: item.date,
13833
- workspace_uuid: item.workspace_id,
13834
- workspace_name: item.workspace_name,
13835
- displayName: item.workspace_display_name || item.display_name || void 0,
13836
- action_count: item.total_output || 0,
13837
- pph: item.avg_pph || 0,
13838
- performance_score: item.performance_score || 0,
13839
- avg_cycle_time: item.avg_cycle_time || 0,
13840
- trend: item.trend_score === 1 ? 2 : 0,
13841
- predicted_output: item.ideal_output || 0,
13842
- efficiency: item.efficiency || 0,
13843
- action_threshold: item.total_day_output || 0,
13844
- show_exclamation: item.show_exclamation ?? void 0,
13845
- monitoring_mode: item.monitoring_mode ?? void 0,
13846
- idle_time: idleTimeSeconds,
13847
- assembly_enabled: item.assembly_enabled ?? false,
13848
- video_grid_metric_mode: normalizeVideoGridMetricMode(
13849
- item.video_grid_metric_mode,
13850
- item.assembly_enabled ?? false
13851
- ),
13852
- action_type: actionType,
13853
- action_family: actionFamily,
13854
- action_display_name: getActionDisplayName({
13855
- displayName: item.action_display_name,
13856
- actionFamily: item.action_family,
13857
- actionType: item.action_type,
13858
- actionName: item.action_name
13859
- }),
13860
- recent_flow_percent: item.recent_flow_percent ?? null,
13861
- recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
13862
- recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
13863
- recent_flow_computed_at: item.recent_flow_computed_at ?? null,
13864
- incoming_wip_current: item.incoming_wip_current ?? null,
13865
- incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
13866
- incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null
13867
- };
13868
- }).sort((a, b) => {
13869
- if (a.line_id !== b.line_id) return a.line_id.localeCompare(b.line_id);
13870
- const wsNumA = parseInt(a.workspace_name?.replace(/[^0-9]/g, "") || "0");
13871
- const wsNumB = parseInt(b.workspace_name?.replace(/[^0-9]/g, "") || "0");
13872
- return wsNumA - wsNumB;
13873
- });
13874
- transformedWorkspaceData.forEach((metric) => {
13875
- workspaceMetricsStore.setOverview(metric);
13930
+ const transformedWorkspaceData = transformMonitorWorkspaceMetrics({
13931
+ rows: allWorkspaceMetrics,
13932
+ companyId: companyId || "",
13933
+ workspaceConfig: effectiveWorkspaceConfig,
13934
+ appTimezone: detailTimezone,
13935
+ lineMetrics: allLineMetrics,
13936
+ resolveShiftConfig: (metricLineId) => isFactoryView ? multiLineShiftConfigMap.get(metricLineId) || staticShiftConfig : shiftConfig || staticShiftConfig,
13937
+ shouldOverrideShiftType: (metricLineId) => isFactoryView ? Boolean(multiLineShiftConfigMap.get(metricLineId)) : Boolean(shiftConfig),
13938
+ fallbackShiftConfig: staticShiftConfig
13876
13939
  });
13877
13940
  const newMetricsState = {
13878
13941
  workspaceMetrics: transformedWorkspaceData,
@@ -13890,6 +13953,7 @@ var useDashboardMetrics = ({
13890
13953
  hydrateFromBackend(idleTimeVlmByLine);
13891
13954
  setMetrics(newMetricsState);
13892
13955
  setMetricsLineId(requestLineId);
13956
+ setMetricsScopeKey(responseScopeKey);
13893
13957
  lastFetchKeyRef.current = inFlightFetchKeyRef.current;
13894
13958
  } catch (err) {
13895
13959
  if (abortController.signal.aborted || err?.name === "AbortError") {
@@ -13912,9 +13976,24 @@ var useDashboardMetrics = ({
13912
13976
  if (requestId === activeRequestIdRef.current) {
13913
13977
  setIsLoading(false);
13914
13978
  isFetchingRef.current = false;
13915
- updateQueueRef.current = false;
13916
13979
  activeFetchLineIdRef.current = null;
13917
13980
  inFlightFetchKeyRef.current = null;
13981
+ if (pendingRealtimeRefreshRef.current) {
13982
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
13983
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS - elapsedSinceLastRealtimeRefresh);
13984
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS, minIntervalRemaining);
13985
+ if (realtimeRefreshTimerRef.current === null) {
13986
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
13987
+ realtimeRefreshTimerRef.current = null;
13988
+ if (!pendingRealtimeRefreshRef.current || isFetchingRef.current || inFlightFetchKeyRef.current) {
13989
+ return;
13990
+ }
13991
+ pendingRealtimeRefreshRef.current = false;
13992
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
13993
+ fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13994
+ }, nextDelay);
13995
+ }
13996
+ }
13918
13997
  }
13919
13998
  }
13920
13999
  }, [
@@ -13944,20 +14023,59 @@ var useDashboardMetrics = ({
13944
14023
  React143.useEffect(() => {
13945
14024
  fetchAllMetricsRef.current = fetchAllMetrics;
13946
14025
  }, [fetchAllMetrics]);
13947
- const queueUpdate = React143.useCallback(() => {
13948
- if (updateQueueRef.current || !supabase) {
13949
- if (updateQueueRef.current) {
13950
- logDebug("[useDashboardMetrics] queueUpdate skipped: update already queued");
14026
+ const clearRealtimeRefreshTimer = React143.useCallback(() => {
14027
+ if (realtimeRefreshTimerRef.current !== null) {
14028
+ window.clearTimeout(realtimeRefreshTimerRef.current);
14029
+ realtimeRefreshTimerRef.current = null;
14030
+ }
14031
+ }, []);
14032
+ const scheduleRealtimeRefresh = React143.useCallback(() => {
14033
+ if (!enabled || !supabase) {
14034
+ pendingRealtimeRefreshRef.current = false;
14035
+ clearRealtimeRefreshTimer();
14036
+ return;
14037
+ }
14038
+ if (!pendingRealtimeRefreshRef.current || realtimeRefreshTimerRef.current !== null) {
14039
+ return;
14040
+ }
14041
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
14042
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS - elapsedSinceLastRealtimeRefresh);
14043
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS, minIntervalRemaining);
14044
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
14045
+ realtimeRefreshTimerRef.current = null;
14046
+ if (!pendingRealtimeRefreshRef.current) {
14047
+ return;
14048
+ }
14049
+ if (isFetchingRef.current || inFlightFetchKeyRef.current) {
14050
+ scheduleRealtimeRefresh();
14051
+ return;
13951
14052
  }
14053
+ pendingRealtimeRefreshRef.current = false;
14054
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
14055
+ fetchAllMetricsRef.current({ force: true, reason: "subscription" });
14056
+ }, nextDelay);
14057
+ }, [clearRealtimeRefreshTimer, enabled, supabase]);
14058
+ React143.useEffect(() => {
14059
+ return () => {
14060
+ clearRealtimeRefreshTimer();
14061
+ pendingRealtimeRefreshRef.current = false;
14062
+ };
14063
+ }, [clearRealtimeRefreshTimer]);
14064
+ const queueUpdate = React143.useCallback(() => {
14065
+ if (!enabled) {
14066
+ logDebug("[useDashboardMetrics] queueUpdate skipped: metrics disabled");
14067
+ return;
14068
+ }
14069
+ if (!supabase) {
13952
14070
  if (!supabase) {
13953
14071
  logDebug("[useDashboardMetrics] queueUpdate skipped: supabase not ready");
13954
14072
  }
13955
14073
  return;
13956
14074
  }
13957
- logDebug("[useDashboardMetrics] queueUpdate triggered from realtime");
13958
- updateQueueRef.current = true;
13959
- fetchAllMetricsRef.current({ force: true, reason: "subscription" });
13960
- }, [supabase, enabled]);
14075
+ pendingRealtimeRefreshRef.current = true;
14076
+ logDebug("[useDashboardMetrics] queueUpdate queued realtime refresh");
14077
+ scheduleRealtimeRefresh();
14078
+ }, [enabled, scheduleRealtimeRefresh, supabase]);
13961
14079
  React143.useEffect(() => {
13962
14080
  if (enabled && lineId && supabase && !shiftLoading && !isTimezoneLoading) {
13963
14081
  fetchAllMetrics({ reason: "line-change" });
@@ -14077,7 +14195,7 @@ var useDashboardMetrics = ({
14077
14195
  { event: "*", schema, table: companySpecificMetricsTable, filter: filter2 },
14078
14196
  (payload) => {
14079
14197
  const payloadData = payload.new || payload.old;
14080
- console.log("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
14198
+ logDebug("[useDashboardMetrics] \u{1F4E1} WS_METRICS payload received:", {
14081
14199
  eventType: payload.eventType,
14082
14200
  lineId: payloadData?.line_id,
14083
14201
  workspaceId: payloadData?.workspace_id,
@@ -14099,14 +14217,14 @@ var useDashboardMetrics = ({
14099
14217
  shiftId: payloadData?.shift_id
14100
14218
  });
14101
14219
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
14102
- console.log("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
14220
+ logDebug("[useDashboardMetrics] \u2705 WS Date/shift match - triggering update");
14103
14221
  queueUpdate();
14104
14222
  } else {
14105
- console.log("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
14223
+ logDebug("[useDashboardMetrics] \u274C WS Date/shift mismatch - update SKIPPED");
14106
14224
  }
14107
14225
  }
14108
14226
  ).subscribe((status) => {
14109
- console.log("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
14227
+ logDebug("[useDashboardMetrics] \u{1F4F6} WS metrics subscription:", {
14110
14228
  channel: wsChannelName,
14111
14229
  status,
14112
14230
  table: companySpecificMetricsTable,
@@ -14124,7 +14242,7 @@ var useDashboardMetrics = ({
14124
14242
  { event: "*", schema, table: configuredLineMetricsTable, filter: filter2 },
14125
14243
  (payload) => {
14126
14244
  const payloadData = payload.new || payload.old;
14127
- console.log("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
14245
+ logDebug("[useDashboardMetrics] \u{1F4E1} LINE_METRICS payload received:", {
14128
14246
  eventType: payload.eventType,
14129
14247
  lineId: payloadData?.line_id,
14130
14248
  date: payloadData?.date,
@@ -14144,15 +14262,15 @@ var useDashboardMetrics = ({
14144
14262
  shiftId: payloadData?.shift_id
14145
14263
  });
14146
14264
  if (payloadData?.date === group.date && payloadData?.shift_id === group.shiftId) {
14147
- console.log("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
14265
+ logDebug("[useDashboardMetrics] \u2705 Date/shift match - triggering update");
14148
14266
  queueUpdate();
14149
14267
  onLineMetricsUpdateRef.current?.();
14150
14268
  } else {
14151
- console.log("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
14269
+ logDebug("[useDashboardMetrics] \u274C Date/shift mismatch - update SKIPPED");
14152
14270
  }
14153
14271
  }
14154
14272
  ).subscribe((status) => {
14155
- console.log("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
14273
+ logDebug("[useDashboardMetrics] \u{1F4F6} Line metrics subscription:", {
14156
14274
  channel: lmChannelName,
14157
14275
  status,
14158
14276
  table: configuredLineMetricsTable,
@@ -14248,7 +14366,7 @@ var useDashboardMetrics = ({
14248
14366
  { event: "*", schema, table, filter: filter2 },
14249
14367
  (payload) => {
14250
14368
  const payloadData = payload.new || payload.old;
14251
- console.log(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
14369
+ logDebug(`[useDashboardMetrics] \u{1F4E1} ${table.toUpperCase()} payload received (single-line):`, {
14252
14370
  eventType: payload.eventType,
14253
14371
  table: payload.table,
14254
14372
  lineId: payloadData?.line_id,
@@ -14272,14 +14390,14 @@ var useDashboardMetrics = ({
14272
14390
  shiftId: payloadData?.shift_id
14273
14391
  });
14274
14392
  if (payloadData?.date === operationalDateForSubscription && payloadData?.shift_id === currentShiftDetails.shiftId) {
14275
- console.log(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
14393
+ logDebug(`[useDashboardMetrics] \u2705 ${table} Date/shift match - triggering update`);
14276
14394
  callback();
14277
14395
  } else {
14278
- console.log(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
14396
+ logDebug(`[useDashboardMetrics] \u274C ${table} Date/shift mismatch - update SKIPPED`);
14279
14397
  }
14280
14398
  }
14281
14399
  ).subscribe((status) => {
14282
- console.log(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
14400
+ logDebug(`[useDashboardMetrics] \u{1F4F6} ${table} subscription:`, {
14283
14401
  channel: channelName,
14284
14402
  status,
14285
14403
  table,
@@ -14328,14 +14446,17 @@ var useDashboardMetrics = ({
14328
14446
  lineId
14329
14447
  // NOTE: userAccessibleLineIds removed - accessed via ref
14330
14448
  ]);
14331
- const isMetricsForActiveLine = metricsLineId === lineId;
14332
- const safeMetrics = isMetricsForActiveLine ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
14449
+ const isCurrentScopeResolved = metricsScopeKey === requestedScopeKey;
14450
+ const hasLastGoodMetrics = metrics2.workspaceMetrics.length > 0 || metrics2.lineMetrics.length > 0;
14451
+ const canReuseLastGoodMetrics = hasLastGoodMetrics && !isCurrentScopeResolved && (isLoading || !!error);
14452
+ const safeMetrics = isCurrentScopeResolved || canReuseLastGoodMetrics ? metrics2 : { workspaceMetrics: [], lineMetrics: [], metadata: void 0, efficiencyLegend: DEFAULT_EFFICIENCY_LEGEND };
14333
14453
  return {
14334
14454
  workspaceMetrics: safeMetrics?.workspaceMetrics || [],
14335
14455
  lineMetrics: safeMetrics?.lineMetrics || [],
14336
14456
  efficiencyLegend: safeMetrics?.efficiencyLegend || DEFAULT_EFFICIENCY_LEGEND,
14337
14457
  metadata: safeMetrics?.metadata,
14338
- isLoading: enabled ? isLoading || !isMetricsForActiveLine : false,
14458
+ isLoading: enabled ? isLoading || !isCurrentScopeResolved && !canReuseLastGoodMetrics : false,
14459
+ isCurrentScopeResolved,
14339
14460
  error,
14340
14461
  refetch: () => {
14341
14462
  if (!enabled) {
@@ -16783,6 +16904,7 @@ var isInitialized = false;
16783
16904
  var isInitializing = false;
16784
16905
  var initializedWithLineIds = [];
16785
16906
  var missingLineContextWarnings = /* @__PURE__ */ new Set();
16907
+ var lineLoadPromises = /* @__PURE__ */ new Map();
16786
16908
  var initializationPromise = null;
16787
16909
  var workspaceDisplayNamesListeners = /* @__PURE__ */ new Set();
16788
16910
  var notifyWorkspaceDisplayNamesListeners = (changedLineId) => {
@@ -16798,6 +16920,30 @@ var subscribeWorkspaceDisplayNames = (listener) => {
16798
16920
  workspaceDisplayNamesListeners.add(listener);
16799
16921
  return () => workspaceDisplayNamesListeners.delete(listener);
16800
16922
  };
16923
+ var storeLineDisplayNames = (lineId, lineDisplayNamesMap) => {
16924
+ runtimeWorkspaceDisplayNames[lineId] = {};
16925
+ lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16926
+ runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16927
+ });
16928
+ };
16929
+ var ensureLineWorkspaceDisplayNamesLoaded = async (lineId) => {
16930
+ if (!lineId || runtimeWorkspaceDisplayNames[lineId]) {
16931
+ return;
16932
+ }
16933
+ const existingPromise = lineLoadPromises.get(lineId);
16934
+ if (existingPromise) {
16935
+ await existingPromise;
16936
+ return;
16937
+ }
16938
+ const loadPromise = workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16939
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
16940
+ notifyWorkspaceDisplayNamesListeners(lineId);
16941
+ }).finally(() => {
16942
+ lineLoadPromises.delete(lineId);
16943
+ });
16944
+ lineLoadPromises.set(lineId, loadPromise);
16945
+ await loadPromise;
16946
+ };
16801
16947
  var getAllWorkspaceDisplayNamesSnapshot = (lineId) => {
16802
16948
  if (lineId && runtimeWorkspaceDisplayNames[lineId]) {
16803
16949
  return { ...runtimeWorkspaceDisplayNames[lineId] };
@@ -16862,6 +17008,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
16862
17008
  }
16863
17009
  console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
16864
17010
  runtimeWorkspaceDisplayNames = {};
17011
+ lineLoadPromises.clear();
16865
17012
  if (targetLineIds.length > 0) {
16866
17013
  const results = await Promise.all(
16867
17014
  targetLineIds.map(async (lineId) => {
@@ -16871,10 +17018,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
16871
17018
  })
16872
17019
  );
16873
17020
  results.forEach(({ lineId, lineDisplayNamesMap }) => {
16874
- runtimeWorkspaceDisplayNames[lineId] = {};
16875
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16876
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16877
- });
17021
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
16878
17022
  console.log(`\u2705 Stored ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16879
17023
  });
16880
17024
  } else {
@@ -16906,13 +17050,8 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
16906
17050
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16907
17051
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching...`);
16908
17052
  try {
16909
- const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
16910
- runtimeWorkspaceDisplayNames[lineId] = {};
16911
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16912
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16913
- });
16914
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16915
- notifyWorkspaceDisplayNamesListeners(lineId);
17053
+ await ensureLineWorkspaceDisplayNamesLoaded(lineId);
17054
+ console.log(`\u2705 Added workspaces for line ${lineId}`);
16916
17055
  } catch (error) {
16917
17056
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16918
17057
  }
@@ -16925,13 +17064,8 @@ var preInitializeWorkspaceDisplayNames = async (lineId) => {
16925
17064
  if (lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16926
17065
  console.log(`\u{1F504} Line ${lineId} not in cache after init, fetching...`);
16927
17066
  try {
16928
- const lineDisplayNamesMap = await workspaceService.getWorkspaceDisplayNames(void 0, lineId);
16929
- runtimeWorkspaceDisplayNames[lineId] = {};
16930
- lineDisplayNamesMap.forEach((displayName, workspaceId) => {
16931
- runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
16932
- });
16933
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId}`);
16934
- notifyWorkspaceDisplayNamesListeners(lineId);
17067
+ await ensureLineWorkspaceDisplayNamesLoaded(lineId);
17068
+ console.log(`\u2705 Added workspaces for line ${lineId}`);
16935
17069
  } catch (error) {
16936
17070
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16937
17071
  }
@@ -16968,13 +17102,8 @@ var getWorkspaceDisplayName = (workspaceId, lineId) => {
16968
17102
  }
16969
17103
  if (isInitialized && lineId && !runtimeWorkspaceDisplayNames[lineId]) {
16970
17104
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching its workspaces...`);
16971
- workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
16972
- runtimeWorkspaceDisplayNames[lineId] = {};
16973
- lineDisplayNamesMap.forEach((displayName2, workspaceId2) => {
16974
- runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
16975
- });
16976
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
16977
- notifyWorkspaceDisplayNamesListeners(lineId);
17105
+ ensureLineWorkspaceDisplayNamesLoaded(lineId).then(() => {
17106
+ console.log(`\u2705 Added workspaces for line ${lineId} to cache`);
16978
17107
  }).catch((error) => {
16979
17108
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
16980
17109
  });
@@ -17016,13 +17145,8 @@ var getShortWorkspaceDisplayName = (workspaceId, lineId) => {
17016
17145
  }
17017
17146
  if (isInitialized && lineId && !runtimeWorkspaceDisplayNames[lineId]) {
17018
17147
  console.log(`\u{1F504} Line ${lineId} not in cache, fetching its workspaces...`);
17019
- workspaceService.getWorkspaceDisplayNames(void 0, lineId).then((lineDisplayNamesMap) => {
17020
- runtimeWorkspaceDisplayNames[lineId] = {};
17021
- lineDisplayNamesMap.forEach((displayName2, workspaceId2) => {
17022
- runtimeWorkspaceDisplayNames[lineId][workspaceId2] = displayName2;
17023
- });
17024
- console.log(`\u2705 Added ${lineDisplayNamesMap.size} workspaces for line ${lineId} to cache`);
17025
- notifyWorkspaceDisplayNamesListeners(lineId);
17148
+ ensureLineWorkspaceDisplayNamesLoaded(lineId).then(() => {
17149
+ console.log(`\u2705 Added workspaces for line ${lineId} to cache`);
17026
17150
  }).catch((error) => {
17027
17151
  console.error(`\u274C Failed to fetch workspaces for line ${lineId}:`, error);
17028
17152
  });
@@ -17102,6 +17226,7 @@ var refreshWorkspaceDisplayNames = async (companyId) => {
17102
17226
  workspaceService.clearWorkspaceDisplayNamesCache();
17103
17227
  runtimeWorkspaceDisplayNames = {};
17104
17228
  isInitialized = false;
17229
+ lineLoadPromises.clear();
17105
17230
  await initializeWorkspaceDisplayNames();
17106
17231
  notifyWorkspaceDisplayNamesListeners();
17107
17232
  };
@@ -17111,6 +17236,7 @@ var clearWorkspaceDisplayNamesCache = () => {
17111
17236
  isInitialized = false;
17112
17237
  isInitializing = false;
17113
17238
  initializedWithLineIds = [];
17239
+ lineLoadPromises.clear();
17114
17240
  initializationPromise = null;
17115
17241
  notifyWorkspaceDisplayNamesListeners();
17116
17242
  };
@@ -17240,7 +17366,16 @@ var useWorkspaceDisplayNamesMap = (workspaceIds, lineId, companyId) => {
17240
17366
  refetch: fetchDisplayNames
17241
17367
  };
17242
17368
  };
17243
- var useWorkspaceVideoStreams = (workspaces) => {
17369
+ var useWorkspaceVideoStreams = (workspaces, options = {}) => {
17370
+ const explicitLineIdsKey = React143.useMemo(() => {
17371
+ const ids = /* @__PURE__ */ new Set();
17372
+ for (const lineId of options.lineIds || []) {
17373
+ if (lineId) {
17374
+ ids.add(lineId);
17375
+ }
17376
+ }
17377
+ return Array.from(ids).sort().join(",");
17378
+ }, [options.lineIds]);
17244
17379
  const workspaceIdsKey = React143.useMemo(() => {
17245
17380
  const ids = /* @__PURE__ */ new Set();
17246
17381
  for (const workspace of workspaces) {
@@ -17250,7 +17385,7 @@ var useWorkspaceVideoStreams = (workspaces) => {
17250
17385
  }
17251
17386
  return Array.from(ids).sort().join(",");
17252
17387
  }, [workspaces]);
17253
- const lineIdsKey = React143.useMemo(() => {
17388
+ const inferredLineIdsKey = React143.useMemo(() => {
17254
17389
  const ids = /* @__PURE__ */ new Set();
17255
17390
  for (const workspace of workspaces) {
17256
17391
  if (workspace.line_id) {
@@ -17259,18 +17394,19 @@ var useWorkspaceVideoStreams = (workspaces) => {
17259
17394
  }
17260
17395
  return Array.from(ids).sort().join(",");
17261
17396
  }, [workspaces]);
17397
+ const lineIdsKey = explicitLineIdsKey || inferredLineIdsKey;
17262
17398
  const requestKey = React143.useMemo(() => {
17263
- if (workspaceIdsKey) {
17264
- return `workspaces:${workspaceIdsKey}`;
17265
- }
17266
17399
  if (lineIdsKey) {
17267
17400
  return `lines:${lineIdsKey}`;
17268
17401
  }
17402
+ if (workspaceIdsKey) {
17403
+ return `workspaces:${workspaceIdsKey}`;
17404
+ }
17269
17405
  return "";
17270
17406
  }, [workspaceIdsKey, lineIdsKey]);
17271
17407
  const workspaceIds = React143.useMemo(
17272
- () => workspaceIdsKey ? workspaceIdsKey.split(",") : [],
17273
- [workspaceIdsKey]
17408
+ () => lineIdsKey ? [] : workspaceIdsKey ? workspaceIdsKey.split(",") : [],
17409
+ [lineIdsKey, workspaceIdsKey]
17274
17410
  );
17275
17411
  const lineIds = React143.useMemo(
17276
17412
  () => lineIdsKey ? lineIdsKey.split(",") : [],
@@ -18814,6 +18950,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18814
18950
  const supabase = useSupabase();
18815
18951
  const databaseConfig = useDatabaseConfig();
18816
18952
  const refreshInterval = options.refreshInterval ?? DEFAULT_REFRESH_INTERVAL_MS;
18953
+ const enabled = options.enabled ?? true;
18817
18954
  const workspaceIdsKey = React143.useMemo(() => {
18818
18955
  const ids = Array.from(new Set(workspaceIds.filter(Boolean)));
18819
18956
  return ids.sort().join(",");
@@ -18824,7 +18961,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18824
18961
  const isFetchingRef = React143.useRef(false);
18825
18962
  const refreshIntervalRef = React143.useRef(null);
18826
18963
  const fetchLastSeen = React143.useCallback(async () => {
18827
- if (!supabase || !workspaceIdsKey || isFetchingRef.current) return;
18964
+ if (!enabled || !supabase || !workspaceIdsKey || isFetchingRef.current) return;
18828
18965
  const healthTable = databaseConfig?.tables?.workspace_health || "workspace_health_status";
18829
18966
  try {
18830
18967
  isFetchingRef.current = true;
@@ -18856,12 +18993,18 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18856
18993
  setIsLoading(false);
18857
18994
  isFetchingRef.current = false;
18858
18995
  }
18859
- }, [supabase, workspaceIdsKey, databaseConfig?.tables?.workspace_health]);
18996
+ }, [databaseConfig?.tables?.workspace_health, enabled, supabase, workspaceIdsKey]);
18860
18997
  React143.useEffect(() => {
18998
+ if (!enabled) {
18999
+ setIsLoading(false);
19000
+ setError(null);
19001
+ setLastSeenByWorkspaceId({});
19002
+ return;
19003
+ }
18861
19004
  fetchLastSeen();
18862
- }, [fetchLastSeen]);
19005
+ }, [enabled, fetchLastSeen]);
18863
19006
  React143.useEffect(() => {
18864
- if (!refreshInterval || !workspaceIdsKey) return;
19007
+ if (!enabled || !refreshInterval || !workspaceIdsKey) return;
18865
19008
  refreshIntervalRef.current = setInterval(() => {
18866
19009
  fetchLastSeen();
18867
19010
  }, refreshInterval);
@@ -18871,7 +19014,7 @@ var useWorkspaceHealthLastSeen = (workspaceIds, options = {}) => {
18871
19014
  refreshIntervalRef.current = null;
18872
19015
  }
18873
19016
  };
18874
- }, [fetchLastSeen, refreshInterval, workspaceIdsKey]);
19017
+ }, [enabled, fetchLastSeen, refreshInterval, workspaceIdsKey]);
18875
19018
  return {
18876
19019
  lastSeenByWorkspaceId,
18877
19020
  isLoading,
@@ -34704,7 +34847,7 @@ var VideoCard = React143__namespace.default.memo(({
34704
34847
  });
34705
34848
  const showOffline = Boolean(isStreamStale);
34706
34849
  const lastSeenText = lastSeenLabel || "Unknown";
34707
- const workspaceDisplayName = displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
34850
+ const workspaceDisplayName = displayName || workspace.displayName || workspace.workspace_name;
34708
34851
  const videoGridMetricValue = getVideoGridMetricValue(workspace);
34709
34852
  const videoGridDisplayValue = getVideoGridDisplayValue(workspace, effectiveLegend, displayMinuteBucket);
34710
34853
  const videoGridColorState = getVideoGridColorState(workspace, effectiveLegend);
@@ -34917,7 +35060,10 @@ var VideoGridView = React143__namespace.default.memo(({
34917
35060
  }
34918
35061
  return Array.from(ids);
34919
35062
  }, [workspaces]);
34920
- const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds);
35063
+ const healthFetchEnabled = !videoStreamsLoading && workspaces.length > 0;
35064
+ const { lastSeenByWorkspaceId } = useWorkspaceHealthLastSeen(workspaceHealthIds, {
35065
+ enabled: healthFetchEnabled
35066
+ });
34921
35067
  React143.useEffect(() => {
34922
35068
  const sample = workspaces.slice(0, 3).map((workspace) => ({
34923
35069
  id: workspace.workspace_uuid || workspace.workspace_name,
@@ -35016,6 +35162,9 @@ var VideoGridView = React143__namespace.default.memo(({
35016
35162
  }, [sortedWorkspaces, lineOrder, lineNames]);
35017
35163
  lineGroups.length > 1;
35018
35164
  const streamsReady = !videoStreamsLoading;
35165
+ const resolveWorkspaceDisplayName = React143.useCallback((workspace) => {
35166
+ return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
35167
+ }, [displayNames]);
35019
35168
  const calculateOptimalGrid = React143.useCallback(() => {
35020
35169
  if (!containerRef.current) return;
35021
35170
  const containerPadding = 16;
@@ -35146,11 +35295,11 @@ var VideoGridView = React143__namespace.default.memo(({
35146
35295
  efficiency: workspace.efficiency,
35147
35296
  action_count: workspace.action_count
35148
35297
  });
35149
- const displayName = workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
35298
+ const displayName = resolveWorkspaceDisplayName(workspace);
35150
35299
  const currentPath = (router$1.asPath || "/").split("#")[0];
35151
35300
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id, currentPath);
35152
35301
  router$1.push(`/workspace/${workspaceId}${navParams}`);
35153
- }, [router$1, prewarmClipsInit]);
35302
+ }, [resolveWorkspaceDisplayName, router$1, prewarmClipsInit]);
35154
35303
  const handleStreamError = React143.useCallback((workspaceId, options) => {
35155
35304
  const isR2Stream = options?.isR2Stream ?? false;
35156
35305
  const hasFallback = Boolean(options?.fallbackUrl);
@@ -35259,7 +35408,7 @@ var VideoGridView = React143__namespace.default.memo(({
35259
35408
  legend: effectiveLegend,
35260
35409
  cropping: card.workspaceCropping,
35261
35410
  canvasFps: effectiveCanvasFps,
35262
- displayName: displayNames[`${card.workspace.line_id}_${card.workspace.workspace_name}`] || getWorkspaceDisplayName(card.workspace.workspace_name, card.workspace.line_id),
35411
+ displayName: resolveWorkspaceDisplayName(card.workspace),
35263
35412
  lastSeenLabel: card.lastSeenLabel,
35264
35413
  useRAF: effectiveUseRAF,
35265
35414
  displayMinuteBucket,
@@ -35271,16 +35420,15 @@ var VideoGridView = React143__namespace.default.memo(({
35271
35420
  },
35272
35421
  card.workspaceKey
35273
35422
  ), [
35274
- displayNames,
35275
35423
  displayMinuteBucket,
35276
35424
  effectiveCanvasFps,
35277
35425
  effectiveLegend,
35278
35426
  effectiveUseRAF,
35279
- getWorkspaceDisplayName,
35280
35427
  handleStreamError,
35281
35428
  handleWorkspaceClick,
35282
35429
  onWorkspaceHover,
35283
35430
  onWorkspaceHoverEnd,
35431
+ resolveWorkspaceDisplayName,
35284
35432
  selectedLine
35285
35433
  ]);
35286
35434
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `relative overflow-hidden h-full w-full bg-slate-50/30 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -35366,6 +35514,9 @@ var MapGridView = React143__namespace.default.memo(({
35366
35514
  prewarmInFlightRef.current.delete(workspaceId);
35367
35515
  });
35368
35516
  }, [dashboardConfig, timezone, supabase]);
35517
+ const resolveWorkspaceDisplayName = React143.useCallback((workspace) => {
35518
+ return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
35519
+ }, [displayNames]);
35369
35520
  const handleWorkspaceClick = React143.useCallback((workspace) => {
35370
35521
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
35371
35522
  prewarmClipsInit(workspace);
@@ -35377,12 +35528,11 @@ var MapGridView = React143__namespace.default.memo(({
35377
35528
  efficiency: workspace.efficiency,
35378
35529
  action_count: workspace.action_count
35379
35530
  });
35380
- const displayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
35381
- getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
35531
+ const displayName = resolveWorkspaceDisplayName(workspace);
35382
35532
  const currentPath = (router$1.asPath || "/").split("#")[0];
35383
35533
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id, currentPath);
35384
35534
  router$1.push(`/workspace/${workspaceId}${navParams}`);
35385
- }, [router$1, displayNames, prewarmClipsInit]);
35535
+ }, [resolveWorkspaceDisplayName, router$1, prewarmClipsInit]);
35386
35536
  const activePositions = React143.useMemo(() => {
35387
35537
  return workspacePositions.filter((pos) => {
35388
35538
  const wsKey = pos.id.toUpperCase();
@@ -35412,7 +35562,18 @@ var MapGridView = React143__namespace.default.memo(({
35412
35562
  workspace_uuid: position.id,
35413
35563
  // Use config ID as temporary UUID
35414
35564
  line_id: "",
35565
+ // Empty line ID
35566
+ company_id: "",
35567
+ shift_id: 0,
35568
+ date: "",
35415
35569
  efficiency: 0,
35570
+ performance_score: 0,
35571
+ action_count: 0,
35572
+ pph: 0,
35573
+ avg_cycle_time: 0,
35574
+ trend: 0,
35575
+ predicted_output: 0,
35576
+ action_threshold: 0,
35416
35577
  show_exclamation: false
35417
35578
  };
35418
35579
  const workspaceId = effectiveWorkspace.workspace_uuid || effectiveWorkspace.workspace_name;
@@ -35421,7 +35582,7 @@ var MapGridView = React143__namespace.default.memo(({
35421
35582
  const performanceColor = isInactivePlaceholder || isConveyorRow ? "bg-slate-200 border-slate-300 text-slate-500" : getPerformanceColor(effectiveWorkspace.efficiency);
35422
35583
  !isInactivePlaceholder && !isConveyorRow && (effectiveWorkspace.show_exclamation ?? (effectiveWorkspace.efficiency < 50 && effectiveWorkspace.efficiency >= 10));
35423
35584
  const configLabel = position.label;
35424
- const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : displayNames[`${effectiveWorkspace.line_id}_${effectiveWorkspace.workspace_name}`] || getWorkspaceDisplayName(effectiveWorkspace.workspace_name, effectiveWorkspace.line_id) || position.id);
35585
+ const workspaceDisplayName = configLabel || (isInactivePlaceholder ? "INACTIVE" : resolveWorkspaceDisplayName(effectiveWorkspace) || position.id);
35425
35586
  return /* @__PURE__ */ jsxRuntime.jsx(
35426
35587
  motion.div,
35427
35588
  {
@@ -39388,9 +39549,12 @@ SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
39388
39549
  var LoadingOverlay = ({
39389
39550
  isVisible,
39390
39551
  message = "Loading...",
39391
- className
39552
+ className,
39553
+ contentClassName,
39554
+ contentVariant = "card"
39392
39555
  }) => {
39393
39556
  if (!isVisible) return null;
39557
+ const contentClasses = contentVariant === "plain" ? "flex flex-col items-center justify-center" : "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl";
39394
39558
  return /* @__PURE__ */ jsxRuntime.jsx(
39395
39559
  motion.div,
39396
39560
  {
@@ -39401,7 +39565,7 @@ var LoadingOverlay = ({
39401
39565
  className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
39402
39566
  "aria-modal": "true",
39403
39567
  role: "dialog",
39404
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message }) })
39568
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${contentClasses} ${contentClassName || ""}`, children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message }) })
39405
39569
  }
39406
39570
  );
39407
39571
  };
@@ -60731,6 +60895,325 @@ var HelpView = ({
60731
60895
  };
60732
60896
  var AuthenticatedHelpView = withAuth(HelpView);
60733
60897
  var HelpView_default = HelpView;
60898
+ var transformActiveBreaks = (activeBreaksByLine) => {
60899
+ if (!activeBreaksByLine) return [];
60900
+ return Object.values(activeBreaksByLine).flat().map((item) => ({
60901
+ lineId: item.line_id,
60902
+ shiftName: item.shift_name || "",
60903
+ startTime: item.start_time,
60904
+ endTime: item.end_time,
60905
+ duration: item.duration || 0,
60906
+ remarks: item.remarks || "",
60907
+ elapsedMinutes: item.elapsed_minutes || 0,
60908
+ remainingMinutes: item.remaining_minutes || 0
60909
+ })).sort((left, right) => {
60910
+ if (left.lineId !== right.lineId) return left.lineId.localeCompare(right.lineId);
60911
+ if (left.shiftName !== right.shiftName) return left.shiftName.localeCompare(right.shiftName);
60912
+ return left.startTime.localeCompare(right.startTime);
60913
+ });
60914
+ };
60915
+ var createEmptyState = () => ({
60916
+ requestKey: null,
60917
+ resolvedScope: [],
60918
+ scopeKey: null,
60919
+ lines: [],
60920
+ workspaceMetrics: [],
60921
+ lineMetrics: [],
60922
+ kpiTrend: null,
60923
+ activeBreaks: [],
60924
+ videoStreamsByWorkspaceId: {},
60925
+ efficiencyLegend: null,
60926
+ metadata: {}
60927
+ });
60928
+ var normalizeMetadata = (metadata) => ({
60929
+ hasFlowBuffers: Boolean(metadata?.has_flow_buffers),
60930
+ idleTimeVlmByLine: metadata?.idle_time_vlm_by_line ?? {},
60931
+ generatedAt: metadata?.generated_at,
60932
+ cacheStatus: metadata?.cache_status,
60933
+ warnings: metadata?.warnings ?? []
60934
+ });
60935
+ var useLiveMonitorBootstrap = ({
60936
+ lineIds,
60937
+ companyId,
60938
+ enabled = true,
60939
+ appTimezone,
60940
+ workspaceConfig,
60941
+ fallbackShiftConfig,
60942
+ lineShiftConfigs
60943
+ }) => {
60944
+ const supabase = useSupabase();
60945
+ const entityConfig = useEntityConfig();
60946
+ const { hydrateFromBackend } = useIdleTimeVlmConfig();
60947
+ const resolvedCompanyId = companyId || entityConfig?.companyId;
60948
+ const effectiveWorkspaceConfig = workspaceConfig || DEFAULT_WORKSPACE_CONFIG;
60949
+ const effectiveTimezone = appTimezone || "Asia/Kolkata";
60950
+ const rawLineIdsKey = (lineIds || []).filter(Boolean).join(",");
60951
+ const normalizedLineIds = React143.useMemo(
60952
+ () => Array.from(new Set(rawLineIdsKey ? rawLineIdsKey.split(",") : [])),
60953
+ [rawLineIdsKey]
60954
+ );
60955
+ const requestKey = React143.useMemo(
60956
+ () => normalizedLineIds.slice().sort().join(","),
60957
+ [normalizedLineIds]
60958
+ );
60959
+ const [state, setState] = React143.useState(() => createEmptyState());
60960
+ const [isLoading, setIsLoading] = React143.useState(false);
60961
+ const [error, setError] = React143.useState(null);
60962
+ const activeRequestIdRef = React143.useRef(0);
60963
+ const fetchBootstrap = React143.useCallback(async (force = false) => {
60964
+ if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
60965
+ return;
60966
+ }
60967
+ const requestId = ++activeRequestIdRef.current;
60968
+ setIsLoading(true);
60969
+ setError(null);
60970
+ try {
60971
+ const searchParams = new URLSearchParams();
60972
+ searchParams.set("company_id", resolvedCompanyId);
60973
+ searchParams.set("line_ids", normalizedLineIds.join(","));
60974
+ if (force) {
60975
+ searchParams.set("force_refresh", "true");
60976
+ }
60977
+ const response = await fetchBackendJson(
60978
+ supabase,
60979
+ `/api/dashboard/monitor-bootstrap?${searchParams.toString()}`,
60980
+ {
60981
+ method: "GET",
60982
+ timeoutMs: 2e4,
60983
+ retries: 0,
60984
+ dedupeKey: `monitor-bootstrap::${requestKey}::${force ? "force" : "cached"}`
60985
+ }
60986
+ );
60987
+ if (requestId !== activeRequestIdRef.current) {
60988
+ return;
60989
+ }
60990
+ const workspaceMetrics = transformMonitorWorkspaceMetrics({
60991
+ rows: response.workspace_metrics || [],
60992
+ companyId: resolvedCompanyId,
60993
+ workspaceConfig: effectiveWorkspaceConfig,
60994
+ appTimezone: effectiveTimezone,
60995
+ lineMetrics: response.line_metrics || [],
60996
+ resolveShiftConfig: (lineId) => lineShiftConfigs?.get(lineId) || fallbackShiftConfig,
60997
+ shouldOverrideShiftType: (lineId) => Boolean(lineShiftConfigs?.get(lineId)),
60998
+ fallbackShiftConfig
60999
+ });
61000
+ const activeBreaks = transformActiveBreaks(response.active_breaks_by_line);
61001
+ const metadata = normalizeMetadata(response.metadata);
61002
+ const workspaceIds = workspaceMetrics.map((metric) => metric.workspace_uuid).filter((workspaceId) => Boolean(workspaceId));
61003
+ const videoStreamsByWorkspaceId = response.video_streams_by_workspace_id || {};
61004
+ if (metadata.idleTimeVlmByLine) {
61005
+ hydrateFromBackend(metadata.idleTimeVlmByLine);
61006
+ }
61007
+ workspaceService.primeWorkspaceVideoStreamsCache({
61008
+ streams: videoStreamsByWorkspaceId,
61009
+ workspaceIds,
61010
+ lineIds: normalizedLineIds
61011
+ });
61012
+ setState({
61013
+ requestKey,
61014
+ resolvedScope: response.resolved_scope || [],
61015
+ scopeKey: response.scope_key || null,
61016
+ lines: response.lines || [],
61017
+ workspaceMetrics,
61018
+ lineMetrics: response.line_metrics || [],
61019
+ kpiTrend: response.kpi_trend || null,
61020
+ activeBreaks,
61021
+ videoStreamsByWorkspaceId,
61022
+ efficiencyLegend: response.efficiency_legend || null,
61023
+ metadata
61024
+ });
61025
+ } catch (fetchError) {
61026
+ if (requestId !== activeRequestIdRef.current) {
61027
+ return;
61028
+ }
61029
+ setError(fetchError instanceof Error ? fetchError : new Error("Failed to load live monitor bootstrap"));
61030
+ } finally {
61031
+ if (requestId === activeRequestIdRef.current) {
61032
+ setIsLoading(false);
61033
+ }
61034
+ }
61035
+ }, [
61036
+ enabled,
61037
+ supabase,
61038
+ resolvedCompanyId,
61039
+ normalizedLineIds,
61040
+ requestKey,
61041
+ hydrateFromBackend,
61042
+ effectiveWorkspaceConfig,
61043
+ effectiveTimezone,
61044
+ lineShiftConfigs,
61045
+ fallbackShiftConfig
61046
+ ]);
61047
+ React143.useEffect(() => {
61048
+ if (!enabled) {
61049
+ setIsLoading(false);
61050
+ return;
61051
+ }
61052
+ if (!resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
61053
+ setState(createEmptyState());
61054
+ setIsLoading(false);
61055
+ setError(null);
61056
+ return;
61057
+ }
61058
+ void fetchBootstrap(false);
61059
+ }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
61060
+ React143.useEffect(() => {
61061
+ if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
61062
+ return void 0;
61063
+ }
61064
+ let intervalId = null;
61065
+ let timeoutId = null;
61066
+ const runMinuteTick = () => {
61067
+ void fetchBootstrap(true);
61068
+ };
61069
+ const startInterval = () => {
61070
+ runMinuteTick();
61071
+ intervalId = window.setInterval(runMinuteTick, 6e4);
61072
+ };
61073
+ const msUntilNextMinute = (() => {
61074
+ const remainder = Date.now() % 6e4;
61075
+ return remainder === 0 ? 6e4 : 6e4 - remainder;
61076
+ })();
61077
+ timeoutId = window.setTimeout(startInterval, msUntilNextMinute);
61078
+ return () => {
61079
+ if (timeoutId !== null) {
61080
+ window.clearTimeout(timeoutId);
61081
+ }
61082
+ if (intervalId !== null) {
61083
+ window.clearInterval(intervalId);
61084
+ }
61085
+ };
61086
+ }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
61087
+ const isCurrentScopeResolved = state.requestKey === requestKey;
61088
+ return {
61089
+ resolvedScope: state.resolvedScope,
61090
+ scopeKey: state.scopeKey,
61091
+ lines: state.lines,
61092
+ workspaceMetrics: state.workspaceMetrics,
61093
+ lineMetrics: state.lineMetrics,
61094
+ kpiTrend: state.kpiTrend,
61095
+ activeBreaks: state.activeBreaks,
61096
+ videoStreamsByWorkspaceId: state.videoStreamsByWorkspaceId,
61097
+ efficiencyLegend: state.efficiencyLegend,
61098
+ metadata: state.metadata,
61099
+ isLoading: enabled ? isLoading || !isCurrentScopeResolved : false,
61100
+ isCurrentScopeResolved,
61101
+ error,
61102
+ refetch: () => fetchBootstrap(true)
61103
+ };
61104
+ };
61105
+
61106
+ // src/lib/utils/liveMonitorBootstrap.ts
61107
+ var toNumber3 = (value) => {
61108
+ if (typeof value === "number" && Number.isFinite(value)) return value;
61109
+ if (typeof value === "string" && value.trim() !== "") {
61110
+ const parsed = Number(value);
61111
+ return Number.isFinite(parsed) ? parsed : 0;
61112
+ }
61113
+ return 0;
61114
+ };
61115
+ var normalizeNullableNumber = (value) => {
61116
+ if (value === null || value === void 0 || value === "") return null;
61117
+ return toNumber3(value);
61118
+ };
61119
+ var getLiveMonitorBootstrapMode = () => {
61120
+ const rawMode = (process.env.NEXT_PUBLIC_MONITOR_BOOTSTRAP_MODE || "").trim().toLowerCase();
61121
+ if (rawMode === "legacy" || rawMode === "shadow" || rawMode === "bootstrap") {
61122
+ return rawMode;
61123
+ }
61124
+ return "bootstrap";
61125
+ };
61126
+ var normalizeMonitorShadowSnapshot = (input) => {
61127
+ const sortedSelectedLineIds = Array.from(new Set((input.selectedLineIds || []).filter(Boolean))).sort();
61128
+ const resolvedScope = (input.resolvedScope || []).filter((entry) => entry?.line_id && entry?.date && entry?.shift_id !== void 0 && entry?.shift_id !== null).map((entry) => ({
61129
+ line_id: entry.line_id,
61130
+ date: entry.date,
61131
+ shift_id: entry.shift_id
61132
+ })).sort((left, right) => {
61133
+ if (left.line_id !== right.line_id) return left.line_id.localeCompare(right.line_id);
61134
+ if (left.date !== right.date) return left.date.localeCompare(right.date);
61135
+ return left.shift_id - right.shift_id;
61136
+ });
61137
+ const lines = (input.lines || []).filter((entry) => entry?.line_id).map((entry) => ({
61138
+ line_id: entry.line_id,
61139
+ line_name: entry.line_name || "Unknown Line"
61140
+ })).sort((left, right) => left.line_id.localeCompare(right.line_id));
61141
+ const workspaces = (input.workspaces || []).map((workspace) => {
61142
+ const workspaceId = workspace.workspace_uuid || null;
61143
+ const stream = workspaceId ? input.videoStreamsByWorkspaceId?.[workspaceId] || null : null;
61144
+ return {
61145
+ workspace_id: workspaceId,
61146
+ line_id: workspace.line_id,
61147
+ workspace_name: workspace.workspace_name,
61148
+ display_name: workspace.displayName || null,
61149
+ efficiency: toNumber3(workspace.efficiency),
61150
+ trend: toNumber3(workspace.trend),
61151
+ show_exclamation: Boolean(workspace.show_exclamation),
61152
+ scheduled_break_active: Boolean(workspace.scheduled_break_active),
61153
+ action_count: toNumber3(workspace.action_count),
61154
+ action_threshold: toNumber3(workspace.action_threshold),
61155
+ predicted_output: toNumber3(workspace.predicted_output),
61156
+ avg_cycle_time: toNumber3(workspace.avg_cycle_time),
61157
+ pph: toNumber3(workspace.pph),
61158
+ recent_flow_percent: normalizeNullableNumber(workspace.recent_flow_percent),
61159
+ incoming_wip_current: normalizeNullableNumber(workspace.incoming_wip_current),
61160
+ video_stream: stream ? {
61161
+ workspace_id: stream.workspace_id,
61162
+ camera_uuid: stream.camera_uuid,
61163
+ stream_camera_uuid: stream.stream_camera_uuid,
61164
+ hls_url: stream.hls_url,
61165
+ crop: stream.crop
61166
+ } : null
61167
+ };
61168
+ }).sort((left, right) => {
61169
+ if (left.line_id !== right.line_id) return left.line_id.localeCompare(right.line_id);
61170
+ return left.workspace_name.localeCompare(right.workspace_name, void 0, { numeric: true });
61171
+ });
61172
+ const activeBreaks = (input.activeBreaks || []).map((activeBreak) => ({
61173
+ lineId: activeBreak.lineId,
61174
+ shiftName: activeBreak.shiftName,
61175
+ startTime: activeBreak.startTime,
61176
+ endTime: activeBreak.endTime,
61177
+ remainingMinutes: toNumber3(activeBreak.remainingMinutes)
61178
+ })).sort((left, right) => {
61179
+ if (left.lineId !== right.lineId) return left.lineId.localeCompare(right.lineId);
61180
+ if (left.shiftName !== right.shiftName) return left.shiftName.localeCompare(right.shiftName);
61181
+ return left.startTime.localeCompare(right.startTime);
61182
+ });
61183
+ return {
61184
+ selectedLineIds: sortedSelectedLineIds,
61185
+ resolvedScope,
61186
+ lines,
61187
+ workspaces,
61188
+ activeBreaks,
61189
+ kpis: input.kpis || null,
61190
+ kpiTrend: input.kpiTrend || null,
61191
+ efficiencyLegend: input.efficiencyLegend || null
61192
+ };
61193
+ };
61194
+ var diffMonitorShadowSnapshots = (legacySnapshot, bootstrapSnapshot) => {
61195
+ const mismatches = [];
61196
+ const compareSection = (section) => {
61197
+ const legacyValue = legacySnapshot[section];
61198
+ const bootstrapValue = bootstrapSnapshot[section];
61199
+ if (JSON.stringify(legacyValue) !== JSON.stringify(bootstrapValue)) {
61200
+ mismatches.push({
61201
+ section,
61202
+ legacy: legacyValue,
61203
+ bootstrap: bootstrapValue
61204
+ });
61205
+ }
61206
+ };
61207
+ compareSection("selectedLineIds");
61208
+ compareSection("resolvedScope");
61209
+ compareSection("lines");
61210
+ compareSection("workspaces");
61211
+ compareSection("activeBreaks");
61212
+ compareSection("kpis");
61213
+ compareSection("kpiTrend");
61214
+ compareSection("efficiencyLegend");
61215
+ return mismatches;
61216
+ };
60734
61217
 
60735
61218
  // src/lib/services/notificationService.ts
60736
61219
  var API_BASE_URL = process.env.NEXT_PUBLIC_BACKEND_URL;
@@ -60872,7 +61355,10 @@ var logDebug3 = (...args) => {
60872
61355
  if (!DEBUG_DASHBOARD_LOGS3) return;
60873
61356
  console.log(...args);
60874
61357
  };
61358
+ var EMPTY_LINE_IDS = [];
61359
+ var EMPTY_WORKSPACES = [];
60875
61360
  var LoadingPageCmp = LoadingPage_default;
61361
+ var LoadingOverlayCmp = LoadingOverlay_default;
60876
61362
  function HomeView({
60877
61363
  defaultLineId,
60878
61364
  factoryViewId,
@@ -61020,6 +61506,7 @@ function HomeView({
61020
61506
  return createNotificationService(supabaseClient);
61021
61507
  }, [supabaseClient]);
61022
61508
  const [bottleneckNotification, setBottleneckNotification] = React143.useState(null);
61509
+ const showBottleneckNotificationRef = React143.useRef(null);
61023
61510
  const [bottleneckModalOpen, setBottleneckModalOpen] = React143.useState(false);
61024
61511
  const [bottleneckModalData, setBottleneckModalData] = React143.useState(null);
61025
61512
  const [diagnosisModalOpen, setDiagnosisModalOpen] = React143.useState(false);
@@ -61030,6 +61517,11 @@ function HomeView({
61030
61517
  dashboardConfig?.shiftConfig
61031
61518
  );
61032
61519
  const shouldEnableMetricsFetch = authStatus === "ready";
61520
+ const liveMonitorMode = React143.useMemo(() => getLiveMonitorBootstrapMode(), []);
61521
+ const isLegacyMonitorMode = liveMonitorMode === "legacy";
61522
+ const isShadowMonitorMode = liveMonitorMode === "shadow";
61523
+ const isBootstrapMonitorMode = liveMonitorMode === "bootstrap";
61524
+ const lastShadowMismatchSignatureRef = React143.useRef(null);
61033
61525
  const handleLineMetricsUpdate = React143.useCallback(() => {
61034
61526
  if (trendRefreshTimerRef.current) {
61035
61527
  window.clearTimeout(trendRefreshTimerRef.current);
@@ -61045,45 +61537,24 @@ function HomeView({
61045
61537
  }
61046
61538
  }, []);
61047
61539
  const {
61048
- workspaceMetrics,
61049
- lineMetrics,
61050
- efficiencyLegend,
61051
- metadata: metricsMetadata,
61052
- isLoading: metricsLoading,
61053
- error: metricsError,
61054
- refetch: refetchMetrics
61540
+ workspaceMetrics: legacyWorkspaceMetrics,
61541
+ lineMetrics: legacyLineMetrics,
61542
+ efficiencyLegend: legacyEfficiencyLegend,
61543
+ metadata: legacyMetricsMetadata,
61544
+ isLoading: legacyMetricsLoading,
61545
+ isCurrentScopeResolved: legacyIsCurrentScopeResolved,
61546
+ error: legacyMetricsError,
61547
+ refetch: refetchLegacyMetrics
61055
61548
  } = useDashboardMetrics({
61056
61549
  lineId: metricsScopeLineId,
61057
61550
  lineIds: selectedLineIds,
61058
61551
  onLineMetricsUpdate: handleLineMetricsUpdate,
61059
61552
  userAccessibleLineIds: visibleLineIds,
61060
- // Pass user's accessible lines for supervisor filtering
61061
- enabled: shouldEnableMetricsFetch
61553
+ enabled: shouldEnableMetricsFetch && !isBootstrapMonitorMode
61062
61554
  });
61063
- const metricsDisplayNames = React143.useMemo(() => {
61064
- const nextDisplayNames = {};
61065
- workspaceMetrics.forEach((workspace) => {
61066
- if (!workspace.displayName) {
61067
- return;
61068
- }
61069
- nextDisplayNames[`${workspace.line_id}_${workspace.workspace_name}`] = workspace.displayName;
61070
- });
61071
- return nextDisplayNames;
61072
- }, [workspaceMetrics]);
61073
- React143.useEffect(() => {
61074
- workspaceMetrics.forEach((workspace) => {
61075
- if (!workspace.displayName) {
61076
- return;
61077
- }
61078
- upsertWorkspaceDisplayNameInCache({
61079
- lineId: workspace.line_id,
61080
- workspaceId: workspace.workspace_name,
61081
- displayName: workspace.displayName
61082
- });
61083
- });
61084
- }, [workspaceMetrics]);
61085
- const trendGroups = React143.useMemo(() => {
61086
- const lineMetricsRows = lineMetrics || [];
61555
+ const legacyTrendGroups = React143.useMemo(() => {
61556
+ if (isBootstrapMonitorMode) return null;
61557
+ const lineMetricsRows = legacyLineMetrics || [];
61087
61558
  if (!lineMetricsRows.length) return null;
61088
61559
  if (selectedLineIds.length > 1) {
61089
61560
  const rowsForLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
@@ -61109,7 +61580,7 @@ function HomeView({
61109
61580
  shiftId: group.shiftId
61110
61581
  }));
61111
61582
  }
61112
- const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
61583
+ const row = lineMetricsRows.find((candidate) => candidate?.line_id === primarySelectedLineId);
61113
61584
  if (!row?.date || row?.shift_id === void 0 || row?.shift_id === null) {
61114
61585
  return null;
61115
61586
  }
@@ -61118,69 +61589,119 @@ function HomeView({
61118
61589
  date: row.date,
61119
61590
  shiftId: row.shift_id
61120
61591
  }];
61121
- }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61122
- const trendOptions = React143.useMemo(() => {
61123
- if (!trendGroups || !userCompanyId) return null;
61592
+ }, [isBootstrapMonitorMode, legacyLineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61593
+ const legacyTrendOptions = React143.useMemo(() => {
61594
+ if (isBootstrapMonitorMode || !legacyTrendGroups || !userCompanyId) return null;
61124
61595
  return {
61125
- groups: trendGroups,
61596
+ groups: legacyTrendGroups,
61126
61597
  companyId: userCompanyId,
61127
61598
  refreshKey: trendRefreshKey,
61128
61599
  forceRefresh: trendRefreshKey > 0
61129
61600
  };
61130
- }, [trendGroups, userCompanyId, trendRefreshKey]);
61131
- const { trend: kpiTrend } = useKpiTrends(trendOptions);
61132
- const hasFlowBuffers = Boolean(metricsMetadata?.hasFlowBuffers);
61601
+ }, [isBootstrapMonitorMode, legacyTrendGroups, userCompanyId, trendRefreshKey]);
61602
+ const { trend: legacyKpiTrend } = useKpiTrends(legacyTrendOptions);
61603
+ const bootstrapMonitor = useLiveMonitorBootstrap({
61604
+ lineIds: selectedLineIds,
61605
+ companyId: userCompanyId,
61606
+ enabled: shouldEnableMetricsFetch && !isLegacyMonitorMode,
61607
+ appTimezone: timezone,
61608
+ workspaceConfig: dashboardConfig?.workspaceConfig,
61609
+ fallbackShiftConfig: dashboardConfig?.shiftConfig,
61610
+ lineShiftConfigs
61611
+ });
61612
+ const currentWorkspaceMetrics = isBootstrapMonitorMode ? bootstrapMonitor.workspaceMetrics : legacyWorkspaceMetrics;
61613
+ const currentLineMetrics = isBootstrapMonitorMode ? bootstrapMonitor.lineMetrics : legacyLineMetrics;
61614
+ const currentEfficiencyLegend = isBootstrapMonitorMode ? bootstrapMonitor.efficiencyLegend : legacyEfficiencyLegend;
61615
+ const currentMetricsMetadata = isBootstrapMonitorMode ? bootstrapMonitor.metadata : legacyMetricsMetadata;
61616
+ const currentMetricsLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyMetricsLoading;
61617
+ const currentIsCurrentScopeResolved = isBootstrapMonitorMode ? bootstrapMonitor.isCurrentScopeResolved : legacyIsCurrentScopeResolved;
61618
+ const currentMetricsError = isBootstrapMonitorMode ? bootstrapMonitor.error : legacyMetricsError;
61619
+ const currentRefetchMetrics = isBootstrapMonitorMode ? bootstrapMonitor.refetch : refetchLegacyMetrics;
61620
+ const metricsDisplayNames = React143.useMemo(() => {
61621
+ const nextDisplayNames = {};
61622
+ currentWorkspaceMetrics.forEach((workspace) => {
61623
+ if (!workspace.displayName) {
61624
+ return;
61625
+ }
61626
+ nextDisplayNames[`${workspace.line_id}_${workspace.workspace_name}`] = workspace.displayName;
61627
+ });
61628
+ return nextDisplayNames;
61629
+ }, [currentWorkspaceMetrics]);
61133
61630
  React143.useEffect(() => {
61134
- const sample = workspaceMetrics.slice(0, 3).map((workspace) => ({
61631
+ currentWorkspaceMetrics.forEach((workspace) => {
61632
+ if (!workspace.displayName) {
61633
+ return;
61634
+ }
61635
+ upsertWorkspaceDisplayNameInCache({
61636
+ lineId: workspace.line_id,
61637
+ workspaceId: workspace.workspace_name,
61638
+ displayName: workspace.displayName
61639
+ });
61640
+ });
61641
+ }, [currentWorkspaceMetrics]);
61642
+ const hasFlowBuffers = Boolean(currentMetricsMetadata?.hasFlowBuffers);
61643
+ React143.useEffect(() => {
61644
+ const sample = currentWorkspaceMetrics.slice(0, 3).map((workspace) => ({
61135
61645
  id: workspace.workspace_uuid || workspace.workspace_name,
61136
61646
  efficiency: workspace.efficiency,
61137
61647
  trend: workspace.trend,
61138
61648
  lineId: workspace.line_id
61139
61649
  }));
61140
61650
  logDebug3("[HomeView] workspaceMetrics update:", {
61141
- count: workspaceMetrics.length,
61142
- isLoading: metricsLoading,
61143
- error: metricsError?.message,
61651
+ count: currentWorkspaceMetrics.length,
61652
+ isLoading: currentMetricsLoading,
61653
+ error: currentMetricsError?.message,
61144
61654
  sample
61145
61655
  });
61146
- }, [workspaceMetrics, metricsLoading, metricsError]);
61147
- const kpis = React143.useMemo(() => {
61148
- const lineMetricsRows = lineMetrics || [];
61656
+ }, [currentWorkspaceMetrics, currentMetricsLoading, currentMetricsError]);
61657
+ const buildDisplayKpis = React143.useCallback((sourceLineMetrics, sourceIsLoading, sourceKpiTrend) => {
61658
+ const lineMetricsRows = sourceLineMetrics || [];
61659
+ let baseKpis = null;
61149
61660
  if (selectedLineIds.length > 1) {
61150
- const rowsForSelectedLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
61151
- if (metricsLoading && rowsForSelectedLines.length === 0) return null;
61152
- return aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
61153
- }
61154
- const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
61155
- if (!row) {
61156
- if (metricsLoading) return null;
61157
- return buildKPIsFromLineMetricsRow(null);
61158
- }
61159
- return buildKPIsFromLineMetricsRow(row);
61160
- }, [lineMetrics, metricsLoading, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61161
- const kpisWithTrend = React143.useMemo(() => {
61162
- if (!kpis) return null;
61163
- if (!kpiTrend) return kpis;
61661
+ const rowsForSelectedLines = lineMetricsRows.filter((row) => selectedLineIdSet.has(row?.line_id));
61662
+ if (sourceIsLoading && rowsForSelectedLines.length === 0) return null;
61663
+ baseKpis = aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
61664
+ } else {
61665
+ const row = lineMetricsRows.find((candidate) => candidate?.line_id === primarySelectedLineId);
61666
+ if (!row) {
61667
+ if (sourceIsLoading) return null;
61668
+ baseKpis = buildKPIsFromLineMetricsRow(null);
61669
+ } else {
61670
+ baseKpis = buildKPIsFromLineMetricsRow(row);
61671
+ }
61672
+ }
61673
+ if (!baseKpis || !sourceKpiTrend) {
61674
+ return baseKpis;
61675
+ }
61164
61676
  return {
61165
- ...kpis,
61677
+ ...baseKpis,
61166
61678
  efficiency: {
61167
- ...kpis.efficiency,
61168
- change: kpiTrend.efficiency?.delta_pp ?? kpis.efficiency.change
61679
+ ...baseKpis.efficiency,
61680
+ change: sourceKpiTrend.efficiency?.delta_pp ?? baseKpis.efficiency.change
61169
61681
  },
61170
61682
  outputProgress: {
61171
- ...kpis.outputProgress,
61172
- change: kpiTrend.outputProgress?.delta_pp ?? kpis.outputProgress.change
61683
+ ...baseKpis.outputProgress,
61684
+ change: sourceKpiTrend.outputProgress?.delta_pp ?? baseKpis.outputProgress.change
61173
61685
  },
61174
61686
  underperformingWorkers: {
61175
- ...kpis.underperformingWorkers,
61176
- change: kpiTrend.underperformingWorkers?.delta_count ?? kpis.underperformingWorkers.change
61687
+ ...baseKpis.underperformingWorkers,
61688
+ change: sourceKpiTrend.underperformingWorkers?.delta_count ?? baseKpis.underperformingWorkers.change
61177
61689
  },
61178
61690
  avgCycleTime: {
61179
- ...kpis.avgCycleTime,
61180
- change: kpiTrend.avgCycleTime?.delta_seconds ?? kpis.avgCycleTime.change
61691
+ ...baseKpis.avgCycleTime,
61692
+ change: sourceKpiTrend.avgCycleTime?.delta_seconds ?? baseKpis.avgCycleTime.change
61181
61693
  }
61182
61694
  };
61183
- }, [kpis, kpiTrend]);
61695
+ }, [primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61696
+ const legacyKpisWithTrend = React143.useMemo(
61697
+ () => buildDisplayKpis(legacyLineMetrics, legacyMetricsLoading, legacyKpiTrend),
61698
+ [buildDisplayKpis, legacyLineMetrics, legacyMetricsLoading, legacyKpiTrend]
61699
+ );
61700
+ const bootstrapKpisWithTrend = React143.useMemo(
61701
+ () => buildDisplayKpis(bootstrapMonitor.lineMetrics, bootstrapMonitor.isLoading, bootstrapMonitor.kpiTrend),
61702
+ [buildDisplayKpis, bootstrapMonitor.lineMetrics, bootstrapMonitor.isLoading, bootstrapMonitor.kpiTrend]
61703
+ );
61704
+ const currentKpisWithTrend = isBootstrapMonitorMode ? bootstrapKpisWithTrend : legacyKpisWithTrend;
61184
61705
  const selectedLineMeta = React143.useMemo(
61185
61706
  () => selectedLineIds.length === 1 ? dbLines.find((line) => line.id === primarySelectedLineId) : void 0,
61186
61707
  [dbLines, primarySelectedLineId, selectedLineIds.length]
@@ -61189,40 +61710,121 @@ function HomeView({
61189
61710
  const isUptimeMode = selectedMonitoringMode === "uptime";
61190
61711
  const averageIdleTimeSeconds = React143.useMemo(() => {
61191
61712
  if (!isUptimeMode) return null;
61192
- const targetWorkspaces = workspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
61713
+ const targetWorkspaces = currentWorkspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
61193
61714
  const idleValues = targetWorkspaces.map((ws) => ws.idle_time).filter((value) => Number.isFinite(value));
61194
61715
  if (idleValues.length === 0) return 0;
61195
61716
  const totalIdle = idleValues.reduce((sum, value) => sum + value, 0);
61196
61717
  return totalIdle / idleValues.length;
61197
- }, [isUptimeMode, primarySelectedLineId, workspaceMetrics]);
61718
+ }, [isUptimeMode, primarySelectedLineId, currentWorkspaceMetrics]);
61198
61719
  const {
61199
- activeBreaks: allActiveBreaks,
61200
- isLoading: breaksLoading,
61201
- error: breaksError
61202
- } = useActiveBreaks(visibleLineIds);
61203
- const activeBreaks = React143.useMemo(() => {
61720
+ activeBreaks: legacyAllActiveBreaks,
61721
+ isLoading: legacyBreaksLoading,
61722
+ error: legacyBreaksError
61723
+ } = useActiveBreaks(isBootstrapMonitorMode ? EMPTY_LINE_IDS : visibleLineIds);
61724
+ const legacyActiveBreaks = React143.useMemo(() => {
61204
61725
  if (isAllLinesSelection(selectedLineIds)) {
61205
- return allActiveBreaks;
61726
+ return legacyAllActiveBreaks;
61206
61727
  }
61207
- return allActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
61208
- }, [allActiveBreaks, selectedLineIdSet, selectedLineIds]);
61728
+ return legacyAllActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
61729
+ }, [legacyAllActiveBreaks, selectedLineIdSet, selectedLineIds]);
61730
+ const currentActiveBreaks = isBootstrapMonitorMode ? bootstrapMonitor.activeBreaks : legacyActiveBreaks;
61731
+ const currentBreaksLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyBreaksLoading;
61732
+ const currentBreaksError = isBootstrapMonitorMode ? bootstrapMonitor.error?.message || null : legacyBreaksError;
61209
61733
  const activeBreakLineIds = React143.useMemo(
61210
- () => new Set(activeBreaks.map((breakItem) => breakItem.lineId)),
61211
- [activeBreaks]
61734
+ () => new Set(currentActiveBreaks.map((breakItem) => breakItem.lineId)),
61735
+ [currentActiveBreaks]
61212
61736
  );
61213
61737
  const workspaceMetricsWithBreakState = React143.useMemo(
61214
- () => workspaceMetrics.map((workspace) => ({
61738
+ () => currentWorkspaceMetrics.map((workspace) => ({
61215
61739
  ...workspace,
61216
61740
  scheduled_break_active: activeBreakLineIds.has(workspace.line_id)
61217
61741
  })),
61218
- [workspaceMetrics, activeBreakLineIds]
61742
+ [currentWorkspaceMetrics, activeBreakLineIds]
61219
61743
  );
61220
61744
  const [breakNotificationsDismissed, setBreakNotificationsDismissed] = React143.useState(false);
61221
61745
  React143.useEffect(() => {
61222
- if (activeBreaks.length > 0) {
61746
+ if (currentActiveBreaks.length > 0) {
61223
61747
  setBreakNotificationsDismissed(false);
61224
61748
  }
61225
- }, [activeBreaks.length]);
61749
+ }, [currentActiveBreaks.length]);
61750
+ const {
61751
+ streamsByWorkspaceId: legacyVideoStreamsByWorkspaceId,
61752
+ isLoading: legacyVideoStreamsLoading
61753
+ } = useWorkspaceVideoStreams(isBootstrapMonitorMode ? EMPTY_WORKSPACES : legacyWorkspaceMetrics, {
61754
+ lineIds: isBootstrapMonitorMode ? EMPTY_LINE_IDS : selectedLineIds
61755
+ });
61756
+ const currentVideoStreamsByWorkspaceId = isBootstrapMonitorMode ? bootstrapMonitor.videoStreamsByWorkspaceId : legacyVideoStreamsByWorkspaceId;
61757
+ const currentVideoStreamsLoading = isBootstrapMonitorMode ? bootstrapMonitor.isLoading : legacyVideoStreamsLoading;
61758
+ React143.useEffect(() => {
61759
+ if (!isShadowMonitorMode) {
61760
+ lastShadowMismatchSignatureRef.current = null;
61761
+ return;
61762
+ }
61763
+ if (!shouldEnableMetricsFetch || legacyMetricsLoading || !legacyIsCurrentScopeResolved || bootstrapMonitor.isLoading || !bootstrapMonitor.isCurrentScopeResolved) {
61764
+ return;
61765
+ }
61766
+ const legacySnapshot = normalizeMonitorShadowSnapshot({
61767
+ selectedLineIds,
61768
+ workspaces: workspaceMetricsWithBreakState,
61769
+ kpis: legacyKpisWithTrend,
61770
+ kpiTrend: legacyKpiTrend,
61771
+ activeBreaks: legacyActiveBreaks,
61772
+ videoStreamsByWorkspaceId: legacyVideoStreamsByWorkspaceId,
61773
+ efficiencyLegend: legacyEfficiencyLegend
61774
+ });
61775
+ const bootstrapSnapshot = normalizeMonitorShadowSnapshot({
61776
+ selectedLineIds,
61777
+ resolvedScope: bootstrapMonitor.resolvedScope,
61778
+ lines: bootstrapMonitor.lines,
61779
+ workspaces: bootstrapMonitor.workspaceMetrics.map((workspace) => ({
61780
+ ...workspace,
61781
+ scheduled_break_active: bootstrapMonitor.activeBreaks.some((activeBreak) => activeBreak.lineId === workspace.line_id)
61782
+ })),
61783
+ kpis: bootstrapKpisWithTrend,
61784
+ kpiTrend: bootstrapMonitor.kpiTrend,
61785
+ activeBreaks: bootstrapMonitor.activeBreaks,
61786
+ videoStreamsByWorkspaceId: bootstrapMonitor.videoStreamsByWorkspaceId,
61787
+ efficiencyLegend: bootstrapMonitor.efficiencyLegend
61788
+ });
61789
+ const mismatches = diffMonitorShadowSnapshots(legacySnapshot, bootstrapSnapshot);
61790
+ const signature = JSON.stringify(mismatches);
61791
+ if (mismatches.length === 0) {
61792
+ lastShadowMismatchSignatureRef.current = null;
61793
+ return;
61794
+ }
61795
+ if (lastShadowMismatchSignatureRef.current === signature) {
61796
+ return;
61797
+ }
61798
+ lastShadowMismatchSignatureRef.current = signature;
61799
+ console.error("[HomeView][monitor-shadow-mismatch]", {
61800
+ selectedLineIds,
61801
+ mismatches,
61802
+ legacySnapshot,
61803
+ bootstrapSnapshot
61804
+ });
61805
+ }, [
61806
+ bootstrapKpisWithTrend,
61807
+ bootstrapMonitor.activeBreaks,
61808
+ bootstrapMonitor.efficiencyLegend,
61809
+ bootstrapMonitor.isCurrentScopeResolved,
61810
+ bootstrapMonitor.isLoading,
61811
+ bootstrapMonitor.kpiTrend,
61812
+ bootstrapMonitor.lines,
61813
+ bootstrapMonitor.resolvedScope,
61814
+ bootstrapMonitor.videoStreamsByWorkspaceId,
61815
+ bootstrapMonitor.workspaceMetrics,
61816
+ isShadowMonitorMode,
61817
+ legacyActiveBreaks,
61818
+ legacyEfficiencyLegend,
61819
+ legacyIsCurrentScopeResolved,
61820
+ legacyKpiTrend,
61821
+ legacyKpisWithTrend,
61822
+ legacyMetricsLoading,
61823
+ legacyVideoStreamsByWorkspaceId,
61824
+ selectedLineIds,
61825
+ shouldEnableMetricsFetch,
61826
+ workspaceMetricsWithBreakState
61827
+ ]);
61226
61828
  const showBottleneckNotification = React143.useCallback(async (bottleneck) => {
61227
61829
  try {
61228
61830
  logDebug3("\u{1F514} [Notification] Raw bottleneck data:", bottleneck);
@@ -61400,6 +62002,9 @@ function HomeView({
61400
62002
  setBottleneckNotification(errorNotification);
61401
62003
  }
61402
62004
  }, [notificationService, timezone, dashboardConfig, lineShiftConfigs]);
62005
+ React143.useEffect(() => {
62006
+ showBottleneckNotificationRef.current = showBottleneckNotification;
62007
+ }, [showBottleneckNotification]);
61403
62008
  React143.useEffect(() => {
61404
62009
  const ticketsEnabled = dashboardConfig?.ticketsConfig?.enabled ?? true;
61405
62010
  if (!ticketsEnabled) {
@@ -61469,7 +62074,7 @@ function HomeView({
61469
62074
  });
61470
62075
  if (latestTicket.event_type === "bottleneck") {
61471
62076
  logDebug3("\u{1F514} [Polling] Showing bottleneck notification for:", latestTicket.log_number);
61472
- showBottleneckNotification(latestTicket);
62077
+ await showBottleneckNotificationRef.current?.(latestTicket);
61473
62078
  } else {
61474
62079
  logDebug3("\u26A0\uFE0F [Polling] Non-bottleneck ticket found but no handler configured:", latestTicket.event_type);
61475
62080
  }
@@ -61489,29 +62094,30 @@ function HomeView({
61489
62094
  clearInterval(pollInterval);
61490
62095
  clearTimeout(initialPollTimer);
61491
62096
  };
61492
- }, [notificationService, userCompanyId, user, showBottleneckNotification, dashboardConfig?.ticketsConfig?.enabled]);
62097
+ }, [
62098
+ dashboardConfig?.ticketsConfig?.enabled,
62099
+ notificationService,
62100
+ user?.email,
62101
+ user?.role_level,
62102
+ userCompanyId
62103
+ ]);
61493
62104
  const handleWorkspaceHover = React143.useCallback((workspaceId) => {
61494
62105
  }, []);
61495
62106
  const handleWorkspaceHoverEnd = React143.useCallback((workspaceId) => {
61496
62107
  }, []);
61497
- const {
61498
- streamsByWorkspaceId: videoStreamsByWorkspaceId,
61499
- isLoading: videoStreamsLoading
61500
- } = useWorkspaceVideoStreams(workspaceMetrics);
61501
- const memoizedKPIs = React143.useMemo(() => kpisWithTrend, [
62108
+ const memoizedKPIs = React143.useMemo(() => currentKpisWithTrend, [
61502
62109
  // Only update reference when values change by at least 1%
61503
- kpisWithTrend?.efficiency?.value ? Math.round(kpisWithTrend.efficiency.value) : null,
61504
- kpisWithTrend?.efficiency?.change,
61505
- kpisWithTrend?.underperformingWorkers?.current,
61506
- kpisWithTrend?.underperformingWorkers?.total,
61507
- kpisWithTrend?.underperformingWorkers?.change,
61508
- kpisWithTrend?.outputProgress?.current,
61509
- kpisWithTrend?.outputProgress?.target,
61510
- kpisWithTrend?.outputProgress?.change,
61511
- kpisWithTrend?.avgCycleTime?.value ? Math.round(kpisWithTrend.avgCycleTime.value * 10) / 10 : null,
61512
- // Round to 1 decimal
61513
- kpisWithTrend?.avgCycleTime?.change,
61514
- kpisWithTrend?.qualityCompliance?.value ? Math.round(kpisWithTrend.qualityCompliance.value) : null,
62110
+ currentKpisWithTrend?.efficiency?.value ? Math.round(currentKpisWithTrend.efficiency.value) : null,
62111
+ currentKpisWithTrend?.efficiency?.change,
62112
+ currentKpisWithTrend?.underperformingWorkers?.current,
62113
+ currentKpisWithTrend?.underperformingWorkers?.total,
62114
+ currentKpisWithTrend?.underperformingWorkers?.change,
62115
+ currentKpisWithTrend?.outputProgress?.current,
62116
+ currentKpisWithTrend?.outputProgress?.target,
62117
+ currentKpisWithTrend?.outputProgress?.change,
62118
+ currentKpisWithTrend?.avgCycleTime?.value ? Math.round(currentKpisWithTrend.avgCycleTime.value * 10) / 10 : null,
62119
+ currentKpisWithTrend?.avgCycleTime?.change,
62120
+ currentKpisWithTrend?.qualityCompliance?.value ? Math.round(currentKpisWithTrend.qualityCompliance.value) : null,
61515
62121
  selectedLineIdsKey
61516
62122
  ]);
61517
62123
  React143.useEffect(() => {
@@ -61522,13 +62128,13 @@ function HomeView({
61522
62128
  dashboard_surface: "monitor"
61523
62129
  });
61524
62130
  }, []);
61525
- const metricsErrorMessage = metricsError?.message || null;
62131
+ const metricsErrorMessage = currentMetricsError?.message || null;
61526
62132
  const handleRetryDashboardData = React143.useCallback(async () => {
61527
62133
  if (isRecoveringSession) {
61528
62134
  await retrySessionHydration();
61529
62135
  }
61530
- refetchMetrics();
61531
- }, [isRecoveringSession, refetchMetrics, retrySessionHydration]);
62136
+ await currentRefetchMetrics();
62137
+ }, [currentRefetchMetrics, isRecoveringSession, retrySessionHydration]);
61532
62138
  const getTrackedLineScope = React143.useCallback((lineIdsForScope) => {
61533
62139
  if (isAllLinesSelection(lineIdsForScope)) {
61534
62140
  return factoryViewId;
@@ -61581,12 +62187,15 @@ function HomeView({
61581
62187
  updateSelectedLineIds(Array.from(currentSelection));
61582
62188
  }, [selectedLineIds, updateSelectedLineIds]);
61583
62189
  React143.useEffect(() => {
61584
- if (!metricsLoading && isChangingFilter) {
62190
+ if (!isChangingFilter) {
62191
+ return;
62192
+ }
62193
+ if (!currentMetricsLoading && currentIsCurrentScopeResolved || currentMetricsError) {
61585
62194
  setIsChangingFilter(false);
61586
62195
  }
61587
- }, [metricsLoading, isChangingFilter]);
62196
+ }, [currentIsCurrentScopeResolved, currentMetricsError, currentMetricsLoading, isChangingFilter]);
61588
62197
  React143.useEffect(() => {
61589
- if (shouldEnableMetricsFetch && !metricsLoading && !metricsError && !hasInitialDataLoaded) {
62198
+ if (shouldEnableMetricsFetch && !currentMetricsLoading && !currentMetricsError && !hasInitialDataLoaded) {
61590
62199
  setHasInitialDataLoaded(true);
61591
62200
  trackCoreEvent("monitor page loaded", {
61592
62201
  default_line_id: defaultLineId,
@@ -61595,7 +62204,7 @@ function HomeView({
61595
62204
  dashboard_surface: "monitor"
61596
62205
  });
61597
62206
  }
61598
- }, [shouldEnableMetricsFetch, metricsLoading, metricsError, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
62207
+ }, [shouldEnableMetricsFetch, currentMetricsLoading, currentMetricsError, hasInitialDataLoaded, defaultLineId, factoryViewId, isSupervisor]);
61599
62208
  const lineTitle = React143.useMemo(() => {
61600
62209
  return factoryName;
61601
62210
  }, [factoryName]);
@@ -61746,15 +62355,15 @@ function HomeView({
61746
62355
  return showLoading;
61747
62356
  };
61748
62357
  const isAuthBootstrapping = authStatus === "loading";
61749
- const isInitialLoading = !isHydrated || !hasInitialDataLoaded && (isAuthBootstrapping || shouldEnableMetricsFetch && metricsLoading);
61750
- const isDataLoading = metricsLoading;
62358
+ const isInitialLoading = !isHydrated || !hasInitialDataLoaded && (isAuthBootstrapping || shouldEnableMetricsFetch && currentMetricsLoading);
62359
+ const isDataLoading = currentMetricsLoading;
61751
62360
  const hasKpiDataReady = React143.useMemo(() => {
61752
- const lineMetricsRows = lineMetrics || [];
62361
+ const lineMetricsRows = currentLineMetrics || [];
61753
62362
  if (selectedLineIds.length > 1) {
61754
62363
  return lineMetricsRows.some((row) => selectedLineIdSet.has(row?.line_id));
61755
62364
  }
61756
62365
  return lineMetricsRows.some((row) => row?.line_id === primarySelectedLineId);
61757
- }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
62366
+ }, [currentLineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
61758
62367
  const isKpiLoading = !hasKpiDataReady;
61759
62368
  const shouldShowReconnectScreen = !hasInitialDataLoaded && (isRecoveringSession || !shouldEnableMetricsFetch && authStatus !== "failed" || !!metricsErrorMessage && authStatus !== "failed");
61760
62369
  const shouldShowFatalLoadFailure = !hasInitialDataLoaded && !!metricsErrorMessage && authStatus === "failed";
@@ -61782,6 +62391,7 @@ function HomeView({
61782
62391
  return () => clearTimeout(timer);
61783
62392
  }, [isDataLoading]);
61784
62393
  const shouldShowDataLoading = showDataLoading || isDataLoading;
62394
+ const shouldShowFilterChangeLoader = isChangingFilter && !currentIsCurrentScopeResolved;
61785
62395
  const shouldShowKpiLoading = useSmoothLoading(isKpiLoading, 400);
61786
62396
  const displayKpis = shouldShowKpiLoading ? null : memoizedKPIs;
61787
62397
  const kpiSectionControl = React143.useMemo(() => /* @__PURE__ */ jsxRuntime.jsx(
@@ -61866,7 +62476,7 @@ function HomeView({
61866
62476
  }
61867
62477
  )
61868
62478
  ] }) }) : null,
61869
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
62479
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
61870
62480
  motion.div,
61871
62481
  {
61872
62482
  initial: { opacity: 0, scale: 0.98 },
@@ -61878,10 +62488,10 @@ function HomeView({
61878
62488
  lineNames: mergedLineNames,
61879
62489
  lineOrder: selectedLineIds,
61880
62490
  factoryView: factoryViewId,
61881
- legend: efficiencyLegend,
62491
+ legend: currentEfficiencyLegend,
61882
62492
  videoSources,
61883
- videoStreamsByWorkspaceId,
61884
- videoStreamsLoading,
62493
+ videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
62494
+ videoStreamsLoading: currentVideoStreamsLoading,
61885
62495
  displayNames: metricsDisplayNames,
61886
62496
  hasFlowBuffers,
61887
62497
  className: "h-full",
@@ -61889,8 +62499,7 @@ function HomeView({
61889
62499
  onWorkspaceHover: handleWorkspaceHover,
61890
62500
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
61891
62501
  })
61892
- },
61893
- selectedLineIdsKey
62502
+ }
61894
62503
  ) : !shouldShowDataLoading && hasInitialDataLoaded ? /* @__PURE__ */ jsxRuntime.jsx(
61895
62504
  motion.div,
61896
62505
  {
@@ -61912,10 +62521,10 @@ function HomeView({
61912
62521
  lineNames: mergedLineNames,
61913
62522
  lineOrder: selectedLineIds,
61914
62523
  factoryView: factoryViewId,
61915
- legend: efficiencyLegend,
62524
+ legend: currentEfficiencyLegend,
61916
62525
  videoSources,
61917
- videoStreamsByWorkspaceId,
61918
- videoStreamsLoading,
62526
+ videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
62527
+ videoStreamsLoading: currentVideoStreamsLoading,
61919
62528
  displayNames: metricsDisplayNames,
61920
62529
  hasFlowBuffers,
61921
62530
  className: "h-full",
@@ -61923,16 +62532,15 @@ function HomeView({
61923
62532
  onWorkspaceHover: handleWorkspaceHover,
61924
62533
  onWorkspaceHoverEnd: handleWorkspaceHoverEnd
61925
62534
  })
61926
- },
61927
- selectedLineIdsKey
62535
+ }
61928
62536
  ) }) })
61929
62537
  ] }),
61930
62538
  /* @__PURE__ */ jsxRuntime.jsx(
61931
62539
  BreakNotificationPopup,
61932
62540
  {
61933
- activeBreaks,
62541
+ activeBreaks: currentActiveBreaks,
61934
62542
  lineNames: mergedLineNames,
61935
- isVisible: !breaksLoading && !breaksError && !breakNotificationsDismissed,
62543
+ isVisible: !currentBreaksLoading && !currentBreaksError && !breakNotificationsDismissed,
61936
62544
  onDismiss: () => setBreakNotificationsDismissed(true)
61937
62545
  }
61938
62546
  ),
@@ -61944,6 +62552,15 @@ function HomeView({
61944
62552
  onDismiss: () => setBottleneckNotification(null)
61945
62553
  }
61946
62554
  ),
62555
+ /* @__PURE__ */ jsxRuntime.jsx(
62556
+ LoadingOverlayCmp,
62557
+ {
62558
+ isVisible: shouldShowFilterChangeLoader,
62559
+ message: "Loading Dashboard...",
62560
+ className: "bg-slate-50/30 backdrop-blur-[3px]",
62561
+ contentVariant: "plain"
62562
+ }
62563
+ ),
61947
62564
  diagnosisModalOpen && bottleneckModalData?.clipId && /* @__PURE__ */ jsxRuntime.jsx(
61948
62565
  DiagnosisVideoModal,
61949
62566
  {
@@ -71500,6 +72117,8 @@ var WorkspaceDetailView = ({
71500
72117
  const totalActions = Number(cachedOverviewMetrics.action_count || 0);
71501
72118
  const targetOutput = Number(cachedOverviewMetrics.action_threshold || 0);
71502
72119
  const idealOutput = Number(cachedOverviewMetrics.predicted_output ?? targetOutput ?? 0);
72120
+ const pphThreshold = Number(cachedOverviewMetrics.pph_threshold || 0);
72121
+ const idealCycleTime = Number(cachedOverviewMetrics.ideal_cycle_time || 0);
71503
72122
  return {
71504
72123
  line_id: cachedOverviewMetrics.line_id,
71505
72124
  line_name: "",
@@ -71518,11 +72137,11 @@ var WorkspaceDetailView = ({
71518
72137
  shift_start: shiftDefinition?.startTime || "",
71519
72138
  shift_end: shiftDefinition?.endTime || "",
71520
72139
  shift_type: shiftType,
71521
- pph_threshold: 0,
72140
+ pph_threshold: pphThreshold,
71522
72141
  target_output: targetOutput,
71523
72142
  avg_pph: Number(cachedOverviewMetrics.pph || 0),
71524
72143
  avg_cycle_time: avgCycleTime,
71525
- ideal_cycle_time: avgCycleTime,
72144
+ ideal_cycle_time: idealCycleTime,
71526
72145
  avg_efficiency: Number(cachedOverviewMetrics.efficiency || 0),
71527
72146
  total_actions: totalActions,
71528
72147
  hourly_action_counts: [],
@@ -74054,7 +74673,8 @@ var TeamManagementView = ({
74054
74673
  const bootstrapData = await bootstrapResponse.json();
74055
74674
  setAvailableFactories((bootstrapData.factories || []).map((factory) => ({
74056
74675
  id: factory.id,
74057
- factory_name: factory.factory_name
74676
+ factory_name: factory.factory_name,
74677
+ company_id: factory.company_id
74058
74678
  })));
74059
74679
  setAvailableLines(bootstrapData.lines || []);
74060
74680
  setUsers(bootstrapData.users || []);
@@ -79022,7 +79642,7 @@ var efficiencyLineConfig = [
79022
79642
  var bumpRenderCounter = (key) => {
79023
79643
  if (process.env.NODE_ENV === "test") ;
79024
79644
  };
79025
- var toNumber3 = (value) => {
79645
+ var toNumber4 = (value) => {
79026
79646
  if (typeof value === "number" && Number.isFinite(value)) return value;
79027
79647
  if (typeof value === "string" && value.trim().length > 0) {
79028
79648
  const parsed = Number(value);
@@ -79557,7 +80177,7 @@ var OverviewSummaryCards = React143__namespace.default.memo(({ store }) => {
79557
80177
  workspaceId: item.workspace_id || "",
79558
80178
  workspaceName: item.workspace_name?.trim() || item.workspace_id || "Unknown",
79559
80179
  lineName: item.line_name?.trim() || "Unknown Line",
79560
- avgIdleSeconds: toNumber3(item.avg_idle_seconds)
80180
+ avgIdleSeconds: toNumber4(item.avg_idle_seconds)
79561
80181
  })).slice(0, 5);
79562
80182
  }, [snapshot.data.summary.avg_idle_per_workstation?.top_contributors]);
79563
80183
  const showIdleContributorLineNames = React143__namespace.default.useMemo(() => {
@@ -79772,9 +80392,9 @@ var PoorestPerformersCard = React143__namespace.default.memo(({
79772
80392
  return {
79773
80393
  id: lineId,
79774
80394
  name: line.line_name?.trim() || "Unknown Line",
79775
- efficiency: roundOne(toNumber3(line.avg_efficiency) || 0),
79776
- previousEfficiency: toNumber3(line.previous_avg_efficiency),
79777
- delta: toNumber3(line.delta_pp),
80395
+ efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
80396
+ previousEfficiency: toNumber4(line.previous_avg_efficiency),
80397
+ delta: toNumber4(line.delta_pp),
79778
80398
  supervisor: supervisor?.displayName || "Unassigned",
79779
80399
  supervisorImage: supervisor?.profilePhotoUrl ?? null
79780
80400
  };
@@ -79883,14 +80503,14 @@ var IdleBreakdownCard = React143__namespace.default.memo(({
79883
80503
  paletteToken: item.palette_token?.trim(),
79884
80504
  iconToken: item.icon_token?.trim(),
79885
80505
  isKnown: item.is_known ?? void 0,
79886
- value: toNumber3(item.percentage) || 0,
79887
- totalDurationSeconds: toNumber3(item.total_duration_seconds),
79888
- efficiencyLossPercentage: toNumber3(item.efficiency_loss_percentage),
80506
+ value: toNumber4(item.percentage) || 0,
80507
+ totalDurationSeconds: toNumber4(item.total_duration_seconds),
80508
+ efficiencyLossPercentage: toNumber4(item.efficiency_loss_percentage),
79889
80509
  contributors: (item.contributors || []).map((contributor) => ({
79890
80510
  workspaceId: contributor.workspace_id || "",
79891
80511
  workspaceName: contributor.workspace_name?.trim() || "Unknown",
79892
- totalDurationSeconds: toNumber3(contributor.total_duration_seconds),
79893
- percentageWithinReason: toNumber3(contributor.percentage_within_reason)
80512
+ totalDurationSeconds: toNumber4(contributor.total_duration_seconds),
80513
+ percentageWithinReason: toNumber4(contributor.percentage_within_reason)
79894
80514
  }))
79895
80515
  })).filter((item) => item.value > 0);
79896
80516
  }, [idle.data]);
@@ -79959,7 +80579,7 @@ var EfficiencyTrendCard = React143__namespace.default.memo(({
79959
80579
  return `${hour12}:${minutes.toString().padStart(2, "0")} ${suffix}`;
79960
80580
  })(),
79961
80581
  efficiency: (() => {
79962
- const value = toNumber3(point.avg_efficiency);
80582
+ const value = toNumber4(point.avg_efficiency);
79963
80583
  return value === null ? void 0 : value;
79964
80584
  })()
79965
80585
  }));
@@ -79972,7 +80592,7 @@ var EfficiencyTrendCard = React143__namespace.default.memo(({
79972
80592
  name: dateFns.format(pointDate, "MMM d"),
79973
80593
  dayOfWeek: dateFns.format(pointDate, "EEEE"),
79974
80594
  efficiency: (() => {
79975
- const value = toNumber3(point.avg_efficiency);
80595
+ const value = toNumber4(point.avg_efficiency);
79976
80596
  return value === null ? void 0 : value;
79977
80597
  })()
79978
80598
  }]];
@@ -79996,7 +80616,7 @@ var EfficiencyTrendCard = React143__namespace.default.memo(({
79996
80616
  name: pointDate ? dateFns.format(pointDate, "MMM d") : "",
79997
80617
  dayOfWeek: pointDate ? dateFns.format(pointDate, "EEEE") : "",
79998
80618
  efficiency: (() => {
79999
- const value = toNumber3(point.avg_efficiency);
80619
+ const value = toNumber4(point.avg_efficiency);
80000
80620
  return value === null ? void 0 : value;
80001
80621
  })()
80002
80622
  };
@@ -80120,7 +80740,7 @@ var debugRefreshLog = (message, payload) => {
80120
80740
  var isAbortError2 = (error) => {
80121
80741
  return error instanceof DOMException && error.name === "AbortError";
80122
80742
  };
80123
- var toNumber4 = (value) => {
80743
+ var toNumber5 = (value) => {
80124
80744
  if (typeof value === "number" && Number.isFinite(value)) return value;
80125
80745
  if (typeof value === "string" && value.trim().length > 0) {
80126
80746
  const parsed = Number(value);
@@ -80129,11 +80749,11 @@ var toNumber4 = (value) => {
80129
80749
  return null;
80130
80750
  };
80131
80751
  var formatImprovementMetric = (recommendation) => {
80132
- const estimatedGain = toNumber4(recommendation.estimated_gain_pieces);
80752
+ const estimatedGain = toNumber5(recommendation.estimated_gain_pieces);
80133
80753
  if (estimatedGain !== null && estimatedGain > 0) {
80134
80754
  return `+${Math.round(estimatedGain).toLocaleString()} pcs / day`;
80135
80755
  }
80136
- const idleMinutes = toNumber4(recommendation.metrics?.idle_minutes);
80756
+ const idleMinutes = toNumber5(recommendation.metrics?.idle_minutes);
80137
80757
  if (idleMinutes !== null) {
80138
80758
  return `-${Math.round(idleMinutes)}m Idle`;
80139
80759
  }