@optifye/dashboard-core 6.12.23 → 6.12.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -14586,6 +14586,32 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
14586
14586
  }
14587
14587
  }
14588
14588
 
14589
+ // src/lib/utils/qaGreenStreakParams.ts
14590
+ var QA_GREEN_STREAK_QUERY_PARAM_NAMES = [
14591
+ "qa_green_streak_started_at",
14592
+ "qa_green_streak_elapsed_seconds",
14593
+ "qa_green_streak_status"
14594
+ ];
14595
+ var isTruthyEnv = (value) => ["1", "true", "yes", "on"].includes(String(value || "").trim().toLowerCase());
14596
+ var isQaGreenStreakForwardingEnabled = () => isTruthyEnv(process.env.NEXT_PUBLIC_DASHBOARD_QA_GREEN_STREAK_OVERRIDE_ENABLED);
14597
+ var appendQaGreenStreakSearchParams = (targetParams) => {
14598
+ if (!isQaGreenStreakForwardingEnabled() || typeof window === "undefined") {
14599
+ return;
14600
+ }
14601
+ const sourceParams = new URLSearchParams(window.location.search);
14602
+ QA_GREEN_STREAK_QUERY_PARAM_NAMES.forEach((paramName) => {
14603
+ const value = sourceParams.get(paramName);
14604
+ if (value !== null) {
14605
+ targetParams.set(paramName, value);
14606
+ }
14607
+ });
14608
+ };
14609
+ var getQaGreenStreakQueryString = () => {
14610
+ const params = new URLSearchParams();
14611
+ appendQaGreenStreakSearchParams(params);
14612
+ return params.toString();
14613
+ };
14614
+
14589
14615
  // src/lib/utils/monitorWorkspaceMetrics.ts
14590
14616
  var sortWorkspaceMetrics = (left, right) => {
14591
14617
  if (left.line_id !== right.line_id) {
@@ -14932,10 +14958,12 @@ var useDashboardMetrics = ({
14932
14958
  let idleTimeVlmByLine = {};
14933
14959
  let efficiencyLegend;
14934
14960
  const forceParam = force ? "&force_refresh=true" : "";
14961
+ const qaGreenStreakParamString = getQaGreenStreakQueryString();
14962
+ const qaGreenStreakParams = qaGreenStreakParamString ? `&${qaGreenStreakParamString}` : "";
14935
14963
  const buildMetricsEndpoint = (params) => {
14936
14964
  const lineIdsParam = isFactory ? `line_ids=${params.groupLineIds.join(",")}` : `line_id=${params.groupLineIds[0]}`;
14937
14965
  const blueComparisonParam = effectiveBlueComparisonLineIds.length ? `&blue_comparison_line_ids=${effectiveBlueComparisonLineIds.join(",")}` : "";
14938
- return `/api/dashboard/metrics?${lineIdsParam}${blueComparisonParam}&date=${params.date}&shift_id=${params.shiftId}&company_id=${companyId}${forceParam}`;
14966
+ return `/api/dashboard/metrics?${lineIdsParam}${blueComparisonParam}&date=${params.date}&shift_id=${params.shiftId}&company_id=${companyId}${forceParam}${qaGreenStreakParams}`;
14939
14967
  };
14940
14968
  if (usesShiftGroups) {
14941
14969
  logDebug("[useDashboardMetrics] Factory view shift groups fetch:", {
@@ -36089,6 +36117,37 @@ var interpretIdleValue = (value) => {
36089
36117
  if (value === "x" || value === null || value === void 0) return "unknown";
36090
36118
  return "unknown";
36091
36119
  };
36120
+ var timeToMinutes = (value) => {
36121
+ const parsed = parseTime(value);
36122
+ if (!parsed) return null;
36123
+ return parsed.hour * 60 + parsed.minute;
36124
+ };
36125
+ var normalizeBreaksOnShiftTimeline2 = (shiftStart, shiftBreaks) => {
36126
+ const shiftStartMinutes = timeToMinutes(shiftStart);
36127
+ if (shiftStartMinutes === null || !Array.isArray(shiftBreaks)) {
36128
+ return [];
36129
+ }
36130
+ return shiftBreaks.flatMap((entry) => {
36131
+ const startRaw = timeToMinutes(entry?.startTime ?? entry?.start);
36132
+ const endRaw = timeToMinutes(entry?.endTime ?? entry?.end);
36133
+ if (startRaw === null || endRaw === null) return [];
36134
+ let start = startRaw;
36135
+ let end = endRaw;
36136
+ if (end <= start) {
36137
+ end += 24 * 60;
36138
+ }
36139
+ if (start < shiftStartMinutes) {
36140
+ start += 24 * 60;
36141
+ end += 24 * 60;
36142
+ }
36143
+ return [{ start, end }];
36144
+ });
36145
+ };
36146
+ var isBreakMinute = (minuteIndex, shiftStartMinutes, normalizedBreaks) => {
36147
+ if (!normalizedBreaks.length) return false;
36148
+ const absoluteMinute = shiftStartMinutes + minuteIndex;
36149
+ return normalizedBreaks.some((entry) => entry.start <= absoluteMinute && absoluteMinute < entry.end);
36150
+ };
36092
36151
  var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
36093
36152
  const start = parseTime(shiftStart);
36094
36153
  const end = parseTime(shiftEnd);
@@ -36099,6 +36158,29 @@ var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
36099
36158
  }
36100
36159
  return duration > 0 ? duration : null;
36101
36160
  };
36161
+ var getBreakExcludedShiftMinutes = ({
36162
+ shiftStart,
36163
+ shiftEnd,
36164
+ elapsedMinutes,
36165
+ shiftBreaks
36166
+ }) => {
36167
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
36168
+ if (shiftMinutes === null) return null;
36169
+ const elapsedLimit = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
36170
+ if (elapsedLimit <= 0) return 0;
36171
+ const startTime = parseTime(shiftStart);
36172
+ if (!startTime) return elapsedLimit;
36173
+ const shiftStartMinutes = startTime.hour * 60 + startTime.minute;
36174
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline2(shiftStart, shiftBreaks);
36175
+ if (!normalizedBreaks.length) return elapsedLimit;
36176
+ let workingMinutes = 0;
36177
+ for (let minuteIndex = 0; minuteIndex < elapsedLimit; minuteIndex += 1) {
36178
+ if (!isBreakMinute(minuteIndex, shiftStartMinutes, normalizedBreaks)) {
36179
+ workingMinutes += 1;
36180
+ }
36181
+ }
36182
+ return workingMinutes;
36183
+ };
36102
36184
  var getShiftElapsedMinutes = ({
36103
36185
  shiftStart,
36104
36186
  shiftEnd,
@@ -36163,7 +36245,8 @@ var buildUptimeSeries = ({
36163
36245
  shiftEnd,
36164
36246
  shiftDate,
36165
36247
  timezone,
36166
- elapsedMinutes
36248
+ elapsedMinutes,
36249
+ shiftBreaks
36167
36250
  }) => {
36168
36251
  const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
36169
36252
  const hasIdleData = Object.keys(normalizedIdle).length > 0;
@@ -36218,6 +36301,8 @@ var buildUptimeSeries = ({
36218
36301
  const points = [];
36219
36302
  let activeMinutes = 0;
36220
36303
  let idleMinutes = 0;
36304
+ const shiftStartMinutes = startTime.hour * 60 + startTime.minute;
36305
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline2(shiftStart, shiftBreaks);
36221
36306
  for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
36222
36307
  const minuteDate = addMinutes(shiftStartDate, minuteIndex);
36223
36308
  const timeLabel = formatInTimeZone(minuteDate, timezone, "h:mm a");
@@ -36230,6 +36315,15 @@ var buildUptimeSeries = ({
36230
36315
  });
36231
36316
  continue;
36232
36317
  }
36318
+ if (isBreakMinute(minuteIndex, shiftStartMinutes, normalizedBreaks)) {
36319
+ points.push({
36320
+ minuteIndex,
36321
+ timeLabel,
36322
+ uptime: null,
36323
+ status: "break"
36324
+ });
36325
+ continue;
36326
+ }
36233
36327
  const hourKey = formatInTimeZone(minuteDate, timezone, "H");
36234
36328
  const minuteKey = Number.parseInt(formatInTimeZone(minuteDate, timezone, "m"), 10);
36235
36329
  const hourBucket = normalizedIdle[hourKey] || [];
@@ -37791,12 +37885,10 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
37791
37885
  const startedAt = workspace.video_grid_green_streak_started_at;
37792
37886
  const anchorAt = workspace.video_grid_green_streak_anchor_at;
37793
37887
  const streakMinutes = workspace.video_grid_green_streak_minutes;
37794
- const computedAt = workspace.recent_flow_computed_at;
37795
37888
  const startedAtMs = parseTimestampMs(startedAt);
37796
37889
  const anchorAtMs = parseTimestampMs(anchorAt);
37797
- const computedAtMs = typeof computedAt === "string" && computedAt.trim() ? parseTimestampMs(computedAt) : Number.NaN;
37798
37890
  const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs2);
37799
- const tickOriginAtMs = Number.isFinite(computedAtMs) ? computedAtMs : anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
37891
+ const tickOriginAtMs = anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
37800
37892
  if (workspace.video_grid_green_streak_active === true && isFiniteNumber3(streakMinutes) && streakMinutes > 0 && startedAt && anchorAt && Number.isFinite(startedAtMs) && isFresh) {
37801
37893
  return {
37802
37894
  startedAt,
@@ -39392,6 +39484,7 @@ var HourlyUptimeChartComponent = ({
39392
39484
  shiftDate,
39393
39485
  timezone,
39394
39486
  elapsedMinutes,
39487
+ shiftBreaks,
39395
39488
  className = ""
39396
39489
  }) => {
39397
39490
  const containerRef = React144__default.useRef(null);
@@ -39403,8 +39496,9 @@ var HourlyUptimeChartComponent = ({
39403
39496
  shiftEnd,
39404
39497
  shiftDate,
39405
39498
  timezone,
39406
- elapsedMinutes
39407
- }), [idleTimeHourly, shiftStart, shiftEnd, shiftDate, timezone, elapsedMinutes]);
39499
+ elapsedMinutes,
39500
+ shiftBreaks
39501
+ }), [idleTimeHourly, shiftStart, shiftEnd, shiftDate, timezone, elapsedMinutes, shiftBreaks]);
39408
39502
  const hasAggregateData = Boolean(hourlyAggregates && hourlyAggregates.length > 0);
39409
39503
  const shiftStartTime = React144__default.useMemo(
39410
39504
  () => getTimeFromTimeString(shiftStart),
@@ -51596,7 +51690,8 @@ var LineMonthlyPdfGenerator = ({
51596
51690
  };
51597
51691
  var LineWhatsAppShareButton = ({
51598
51692
  lineInfo,
51599
- className
51693
+ className,
51694
+ shiftBreaks = []
51600
51695
  }) => {
51601
51696
  const handleShare = () => {
51602
51697
  trackCoreEvent("Line WhatsApp Share Clicked", {
@@ -51610,7 +51705,8 @@ var LineWhatsAppShareButton = ({
51610
51705
  shiftStart: lineInfo.metrics.shift_start,
51611
51706
  shiftEnd: lineInfo.metrics.shift_end,
51612
51707
  shiftDate: lineInfo.date,
51613
- timezone: "Asia/Kolkata"
51708
+ timezone: "Asia/Kolkata",
51709
+ shiftBreaks
51614
51710
  }) : null;
51615
51711
  const efficiencyValue = Number.isFinite(lineInfo.metrics.avg_efficiency) ? Number(lineInfo.metrics.avg_efficiency) : null;
51616
51712
  const utilization = efficiencyValue !== null ? efficiencyValue : uptimeSeries && uptimeSeries.availableMinutes > 0 ? uptimeSeries.activeMinutes / uptimeSeries.availableMinutes * 100 : 0;
@@ -51779,16 +51875,24 @@ var LinePdfGenerator = ({
51779
51875
  shiftStart,
51780
51876
  shiftEnd,
51781
51877
  shiftDate,
51782
- timezone: effectiveUptimeTimezone
51878
+ timezone: effectiveUptimeTimezone,
51879
+ shiftBreaks
51783
51880
  });
51784
51881
  let activeMinutes = uptimeSeries.activeMinutes;
51785
51882
  let idleMinutes = uptimeSeries.idleMinutes;
51786
51883
  let availableMinutes = uptimeSeries.availableMinutes;
51787
51884
  let hasData = uptimeSeries.hasData;
51788
- if (!hasData) {
51885
+ const hasIdleHourlyPayload = Boolean(
51886
+ workspace.idle_time_hourly && typeof workspace.idle_time_hourly === "object" && Object.keys(workspace.idle_time_hourly).length > 0
51887
+ );
51888
+ if (!hasData && !hasIdleHourlyPayload) {
51789
51889
  const idleTimeValue = workspace.idle_time;
51790
51890
  const hasIdleTimeValue = Number.isFinite(idleTimeValue);
51791
- const fallbackDuration = shiftDurationMinutes;
51891
+ const fallbackDuration = getBreakExcludedShiftMinutes({
51892
+ shiftStart,
51893
+ shiftEnd,
51894
+ shiftBreaks
51895
+ }) ?? shiftDurationMinutes;
51792
51896
  if (hasIdleTimeValue && idleTimeValue > 0 && fallbackDuration > 0) {
51793
51897
  const idleFromAggregate = Math.min(Math.max(Number(idleTimeValue) / 60, 0), fallbackDuration);
51794
51898
  idleMinutes = idleFromAggregate;
@@ -51860,7 +51964,8 @@ var LinePdfGenerator = ({
51860
51964
  shiftStart: lineShiftStart,
51861
51965
  shiftEnd: lineShiftEnd,
51862
51966
  shiftDate: lineInfo.date,
51863
- timezone: effectiveUptimeTimezone
51967
+ timezone: effectiveUptimeTimezone,
51968
+ shiftBreaks
51864
51969
  });
51865
51970
  hourlyData = buildHourlyFromSeries(lineUptimeSeries);
51866
51971
  }
@@ -53799,7 +53904,8 @@ var WorkspaceMonthlyHistory = ({
53799
53904
  };
53800
53905
  var WorkspaceWhatsAppShareButton = ({
53801
53906
  workspace,
53802
- className
53907
+ className,
53908
+ shiftBreaks = []
53803
53909
  }) => {
53804
53910
  const handleShare = () => {
53805
53911
  trackCoreEvent("Workspace WhatsApp Share Clicked", {
@@ -53815,7 +53921,11 @@ var WorkspaceWhatsAppShareButton = ({
53815
53921
  timeZone: "Asia/Kolkata"
53816
53922
  });
53817
53923
  const isUptimeMode = workspace.monitoring_mode === "uptime";
53818
- const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
53924
+ const shiftMinutes = getBreakExcludedShiftMinutes({
53925
+ shiftStart: workspace.shift_start,
53926
+ shiftEnd: workspace.shift_end,
53927
+ shiftBreaks
53928
+ }) ?? getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
53819
53929
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
53820
53930
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
53821
53931
  const clampedIdleSeconds = shiftSeconds > 0 ? Math.min(idleSeconds, shiftSeconds) : idleSeconds;
@@ -53905,7 +54015,11 @@ var WorkspacePdfGenerator = ({
53905
54015
  try {
53906
54016
  const isUptimeMode = workspace.monitoring_mode === "uptime";
53907
54017
  const isAssemblyCycleMode = !isUptimeMode && shouldUseAssemblyCycleTimeLayout(workspace);
53908
- const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
54018
+ const shiftMinutes = getBreakExcludedShiftMinutes({
54019
+ shiftStart: workspace.shift_start,
54020
+ shiftEnd: workspace.shift_end,
54021
+ shiftBreaks
54022
+ }) ?? getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
53909
54023
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
53910
54024
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
53911
54025
  const clampedIdleSeconds = shiftSeconds > 0 ? Math.min(idleSeconds, shiftSeconds) : idleSeconds;
@@ -54075,7 +54189,8 @@ var WorkspacePdfGenerator = ({
54075
54189
  shiftStart: workspace.shift_start,
54076
54190
  shiftEnd: workspace.shift_end,
54077
54191
  shiftDate: workspace.date,
54078
- timezone: reportTimezone
54192
+ timezone: reportTimezone,
54193
+ shiftBreaks
54079
54194
  }) : null;
54080
54195
  const hourlyUptime = uptimeSeries?.points?.length ? Array.from({ length: Math.ceil(uptimeSeries.shiftMinutes / 60) }, (_, index) => {
54081
54196
  const start = index * 60;
@@ -63935,6 +64050,8 @@ var HelpView = ({
63935
64050
  };
63936
64051
  var AuthenticatedHelpView = withAuth(HelpView);
63937
64052
  var HelpView_default = HelpView;
64053
+ var REALTIME_REFRESH_DEBOUNCE_MS2 = 1500;
64054
+ var REALTIME_REFRESH_MIN_INTERVAL_MS2 = 5e3;
63938
64055
  var transformActiveBreaks = (activeBreaksByLine) => {
63939
64056
  if (!activeBreaksByLine) return [];
63940
64057
  return Object.values(activeBreaksByLine).flat().map((item) => ({
@@ -63973,6 +64090,17 @@ var normalizeMetadata = (metadata) => ({
63973
64090
  cacheStatus: metadata?.cache_status,
63974
64091
  warnings: metadata?.warnings ?? []
63975
64092
  });
64093
+ var createResolvedScopeLookup = (resolvedScope, workspaces, blueComparisonWorkspaces) => {
64094
+ const lookup = /* @__PURE__ */ new Set();
64095
+ const addEntry = (lineId, date, shiftId) => {
64096
+ if (!lineId || !date || shiftId === void 0 || shiftId === null) return;
64097
+ lookup.add(`${lineId}|${date}|${shiftId}`);
64098
+ };
64099
+ resolvedScope.forEach((entry) => addEntry(entry.line_id, entry.date, entry.shift_id));
64100
+ workspaces.forEach((workspace) => addEntry(workspace.line_id, workspace.date, workspace.shift_id));
64101
+ blueComparisonWorkspaces.forEach((workspace) => addEntry(workspace.line_id, workspace.date, workspace.shift_id));
64102
+ return lookup;
64103
+ };
63976
64104
  var useLiveMonitorBootstrap = ({
63977
64105
  lineIds,
63978
64106
  blueComparisonLineIds,
@@ -63999,20 +64127,34 @@ var useLiveMonitorBootstrap = ({
63999
64127
  () => Array.from(new Set(rawBlueComparisonLineIdsKey ? rawBlueComparisonLineIdsKey.split(",") : [])),
64000
64128
  [rawBlueComparisonLineIdsKey]
64001
64129
  );
64130
+ const realtimeLineIds = useMemo(
64131
+ () => Array.from(new Set([...normalizedLineIds, ...normalizedBlueComparisonLineIds].filter(Boolean))).sort(),
64132
+ [normalizedLineIds, normalizedBlueComparisonLineIds]
64133
+ );
64002
64134
  const requestKey = useMemo(
64003
64135
  () => `${normalizedLineIds.slice().sort().join(",")}|blue:${normalizedBlueComparisonLineIds.slice().sort().join(",")}`,
64004
64136
  [normalizedLineIds, normalizedBlueComparisonLineIds]
64005
64137
  );
64138
+ const realtimeLineIdsKey = useMemo(() => realtimeLineIds.join(","), [realtimeLineIds]);
64139
+ const companySpecificMetricsTable = useMemo(
64140
+ () => getCompanyMetricsTableName(resolvedCompanyId, "performance_metrics"),
64141
+ [resolvedCompanyId]
64142
+ );
64006
64143
  const [state, setState] = useState(() => createEmptyState());
64007
64144
  const [rawState, setRawState] = useState(null);
64008
64145
  const [isLoading, setIsLoading] = useState(false);
64009
64146
  const [error, setError] = useState(null);
64010
64147
  const activeRequestIdRef = useRef(0);
64148
+ const isFetchingRef = useRef(false);
64149
+ const pendingRealtimeRefreshRef = useRef(false);
64150
+ const realtimeRefreshTimerRef = useRef(null);
64151
+ const lastRealtimeRefreshStartedAtRef = useRef(0);
64011
64152
  const fetchBootstrap = useCallback(async (force = false) => {
64012
64153
  if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
64013
64154
  return;
64014
64155
  }
64015
64156
  const requestId = ++activeRequestIdRef.current;
64157
+ isFetchingRef.current = true;
64016
64158
  setIsLoading(true);
64017
64159
  setError(null);
64018
64160
  try {
@@ -64025,6 +64167,7 @@ var useLiveMonitorBootstrap = ({
64025
64167
  if (force) {
64026
64168
  searchParams.set("force_refresh", "true");
64027
64169
  }
64170
+ appendQaGreenStreakSearchParams(searchParams);
64028
64171
  const response = await fetchBackendJson(
64029
64172
  supabase,
64030
64173
  `/api/dashboard/monitor-bootstrap?${searchParams.toString()}`,
@@ -64062,6 +64205,7 @@ var useLiveMonitorBootstrap = ({
64062
64205
  } finally {
64063
64206
  if (requestId === activeRequestIdRef.current) {
64064
64207
  setIsLoading(false);
64208
+ isFetchingRef.current = false;
64065
64209
  }
64066
64210
  }
64067
64211
  }, [
@@ -64072,6 +64216,52 @@ var useLiveMonitorBootstrap = ({
64072
64216
  normalizedBlueComparisonLineIds,
64073
64217
  requestKey
64074
64218
  ]);
64219
+ const fetchBootstrapRef = useRef(fetchBootstrap);
64220
+ useEffect(() => {
64221
+ fetchBootstrapRef.current = fetchBootstrap;
64222
+ }, [fetchBootstrap]);
64223
+ const clearRealtimeRefreshTimer = useCallback(() => {
64224
+ if (realtimeRefreshTimerRef.current !== null) {
64225
+ window.clearTimeout(realtimeRefreshTimerRef.current);
64226
+ realtimeRefreshTimerRef.current = null;
64227
+ }
64228
+ }, []);
64229
+ const scheduleRealtimeRefresh = useCallback(() => {
64230
+ if (!enabled || !supabase || realtimeRefreshTimerRef.current !== null) {
64231
+ return;
64232
+ }
64233
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
64234
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS2 - elapsedSinceLastRealtimeRefresh);
64235
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS2, minIntervalRemaining);
64236
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
64237
+ realtimeRefreshTimerRef.current = null;
64238
+ if (!pendingRealtimeRefreshRef.current) {
64239
+ return;
64240
+ }
64241
+ if (isFetchingRef.current) {
64242
+ scheduleRealtimeRefresh();
64243
+ return;
64244
+ }
64245
+ pendingRealtimeRefreshRef.current = false;
64246
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
64247
+ void fetchBootstrapRef.current(true);
64248
+ }, nextDelay);
64249
+ }, [enabled, supabase]);
64250
+ const queueRealtimeRefresh = useCallback(() => {
64251
+ if (!enabled || !supabase) {
64252
+ pendingRealtimeRefreshRef.current = false;
64253
+ clearRealtimeRefreshTimer();
64254
+ return;
64255
+ }
64256
+ pendingRealtimeRefreshRef.current = true;
64257
+ scheduleRealtimeRefresh();
64258
+ }, [clearRealtimeRefreshTimer, enabled, scheduleRealtimeRefresh, supabase]);
64259
+ useEffect(() => {
64260
+ return () => {
64261
+ pendingRealtimeRefreshRef.current = false;
64262
+ clearRealtimeRefreshTimer();
64263
+ };
64264
+ }, [clearRealtimeRefreshTimer]);
64075
64265
  useEffect(() => {
64076
64266
  if (!enabled || !rawState || !resolvedCompanyId) {
64077
64267
  return;
@@ -64150,6 +64340,83 @@ var useLiveMonitorBootstrap = ({
64150
64340
  }
64151
64341
  void fetchBootstrap(false);
64152
64342
  }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
64343
+ const realtimeScopeKey = useMemo(() => Array.from(createResolvedScopeLookup(
64344
+ state.resolvedScope,
64345
+ state.workspaceMetrics,
64346
+ state.blueComparisonWorkspaceMetrics
64347
+ )).sort().join("||"), [state.resolvedScope, state.workspaceMetrics, state.blueComparisonWorkspaceMetrics]);
64348
+ useEffect(() => {
64349
+ if (!enabled || !resolvedCompanyId || !realtimeLineIdsKey || !supabase) {
64350
+ return void 0;
64351
+ }
64352
+ const scopeLookup = new Set(realtimeScopeKey ? realtimeScopeKey.split("||") : []);
64353
+ if (scopeLookup.size === 0) {
64354
+ return void 0;
64355
+ }
64356
+ const realtimeLineIdsForFilter = realtimeLineIdsKey.split(",").filter(Boolean);
64357
+ if (realtimeLineIdsForFilter.length === 0) {
64358
+ return void 0;
64359
+ }
64360
+ const lineIdFilter = `line_id=in.(${realtimeLineIdsForFilter.join(",")})`;
64361
+ const channels = [];
64362
+ const shouldRefreshForPayload = (payload) => {
64363
+ const payloadData = payload.new || payload.old;
64364
+ return scopeLookup.has(`${payloadData?.line_id}|${payloadData?.date}|${payloadData?.shift_id}`);
64365
+ };
64366
+ const createSubscription = (table, channelNameBase) => {
64367
+ const channelName = `${channelNameBase}-${state.scopeKey || requestKey}`.replace(/[^a-zA-Z0-9_-]/g, "");
64368
+ const channel = supabase.channel(channelName).on(
64369
+ "postgres_changes",
64370
+ { event: "*", schema: "public", table, filter: lineIdFilter },
64371
+ (payload) => {
64372
+ if (shouldRefreshForPayload(payload)) {
64373
+ queueRealtimeRefresh();
64374
+ }
64375
+ }
64376
+ ).subscribe();
64377
+ channels.push(channel);
64378
+ };
64379
+ createSubscription(companySpecificMetricsTable, "monitor-bootstrap-ws");
64380
+ createSubscription("line_metrics", "monitor-bootstrap-lm");
64381
+ createSubscription("wip_buffer_metrics", "monitor-bootstrap-wip-metrics");
64382
+ createSubscription("wip_buffer_alerts", "monitor-bootstrap-wip-alerts");
64383
+ return () => {
64384
+ channels.forEach((channel) => {
64385
+ supabase.removeChannel(channel);
64386
+ });
64387
+ };
64388
+ }, [
64389
+ enabled,
64390
+ resolvedCompanyId,
64391
+ realtimeLineIdsKey,
64392
+ supabase,
64393
+ realtimeScopeKey,
64394
+ state.scopeKey,
64395
+ requestKey,
64396
+ companySpecificMetricsTable,
64397
+ queueRealtimeRefresh
64398
+ ]);
64399
+ useEffect(() => {
64400
+ if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
64401
+ return void 0;
64402
+ }
64403
+ const forceRefreshOnResume = () => {
64404
+ void fetchBootstrapRef.current(true);
64405
+ };
64406
+ const handleVisibilityChange = () => {
64407
+ if (document.visibilityState === "visible") {
64408
+ forceRefreshOnResume();
64409
+ }
64410
+ };
64411
+ document.addEventListener("visibilitychange", handleVisibilityChange);
64412
+ window.addEventListener("focus", forceRefreshOnResume);
64413
+ window.addEventListener("online", forceRefreshOnResume);
64414
+ return () => {
64415
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
64416
+ window.removeEventListener("focus", forceRefreshOnResume);
64417
+ window.removeEventListener("online", forceRefreshOnResume);
64418
+ };
64419
+ }, [enabled, normalizedLineIds.length, resolvedCompanyId, supabase]);
64153
64420
  useEffect(() => {
64154
64421
  if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
64155
64422
  return void 0;
@@ -64904,7 +65171,6 @@ function HomeView({
64904
65171
  const allGreenMilestoneTrackingRef = useRef({ identity: null, lastMilestoneSeconds: null });
64905
65172
  const allGreenStreakTimerBaselineRef = useRef(null);
64906
65173
  const [showAllGreenCelebration, setShowAllGreenCelebration] = useState(false);
64907
- const [allGreenCelebrationStartedAtMs, setAllGreenCelebrationStartedAtMs] = useState(null);
64908
65174
  const [greenStreakMilestoneBanner, setGreenStreakMilestoneBanner] = useState(null);
64909
65175
  const [allGreenStreakNowMs, setAllGreenStreakNowMs] = useState(() => Date.now());
64910
65176
  const currentAllGreenStreakNowMs = Math.max(allGreenStreakNowMs, Date.now());
@@ -64967,13 +65233,11 @@ function HomeView({
64967
65233
  };
64968
65234
  }, [allGreenMilestoneIdentity, allGreenStreakDisplay, currentAllGreenStreakNowMs]);
64969
65235
  const allGreenCelebrationTimerText = useMemo(() => {
64970
- if (!showAllGreenCelebration || allGreenCelebrationStartedAtMs === null) {
65236
+ if (!showAllGreenCelebration || !visibleAllGreenStreakDisplay) {
64971
65237
  return null;
64972
65238
  }
64973
- return formatAllGreenCelebrationTimer(
64974
- Math.max(0, Math.floor((currentAllGreenStreakNowMs - allGreenCelebrationStartedAtMs) / 1e3))
64975
- );
64976
- }, [allGreenCelebrationStartedAtMs, currentAllGreenStreakNowMs, showAllGreenCelebration]);
65239
+ return formatAllGreenCelebrationTimer(visibleAllGreenStreakDisplay.elapsedSeconds);
65240
+ }, [showAllGreenCelebration, visibleAllGreenStreakDisplay]);
64977
65241
  useEffect(() => {
64978
65242
  const hasPossibleStreakTimer = showAllGreenCelebration || Boolean(allGreenStreakDisplay) || workspaceMetricsWithBreakState.some((workspace) => workspace.video_grid_green_streak_active === true);
64979
65243
  if (!hasPossibleStreakTimer) {
@@ -64992,17 +65256,14 @@ function HomeView({
64992
65256
  allGreenCelebrationTimerRef.current = null;
64993
65257
  }
64994
65258
  setShowAllGreenCelebration(false);
64995
- setAllGreenCelebrationStartedAtMs(null);
64996
65259
  }, []);
64997
65260
  const triggerAllGreenCelebration = useCallback(() => {
64998
- setAllGreenCelebrationStartedAtMs(Date.now());
64999
65261
  setShowAllGreenCelebration(true);
65000
65262
  if (allGreenCelebrationTimerRef.current) {
65001
65263
  clearTimeout(allGreenCelebrationTimerRef.current);
65002
65264
  }
65003
65265
  allGreenCelebrationTimerRef.current = setTimeout(() => {
65004
65266
  setShowAllGreenCelebration(false);
65005
- setAllGreenCelebrationStartedAtMs(null);
65006
65267
  allGreenCelebrationTimerRef.current = null;
65007
65268
  }, ALL_GREEN_CELEBRATION_DURATION_MS);
65008
65269
  }, []);
@@ -67724,6 +67985,7 @@ var UptimeBottomSection = memo$1(({
67724
67985
  shiftDate,
67725
67986
  timezone,
67726
67987
  elapsedMinutes,
67988
+ shiftBreaks,
67727
67989
  urlDate,
67728
67990
  urlShift,
67729
67991
  navigate
@@ -67811,7 +68073,8 @@ var UptimeBottomSection = memo$1(({
67811
68073
  shiftEnd,
67812
68074
  shiftDate,
67813
68075
  timezone,
67814
- elapsedMinutes
68076
+ elapsedMinutes,
68077
+ shiftBreaks
67815
68078
  }
67816
68079
  ) })
67817
68080
  ] })
@@ -68129,7 +68392,8 @@ var KPIDetailView = ({
68129
68392
  shiftStart: resolvedShiftStart || void 0,
68130
68393
  shiftEnd: resolvedShiftEnd || void 0,
68131
68394
  shiftDate: metric.date,
68132
- timezone: configuredTimezone
68395
+ timezone: configuredTimezone,
68396
+ shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === metric.shift_id)?.breaks || []
68133
68397
  }) : null;
68134
68398
  const idleTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metricIdleTimeSeconds ?? 0 : (uptimeSeries2?.idleMinutes || 0) * 60 : 0;
68135
68399
  const activeTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metricActiveTimeSeconds ?? 0 : (uptimeSeries2?.activeMinutes || 0) * 60 : 0;
@@ -68309,6 +68573,10 @@ var KPIDetailView = ({
68309
68573
  end: chartMetrics.shift_end || fallback.end
68310
68574
  };
68311
68575
  }, [chartMetrics?.shift_start, chartMetrics?.shift_end, chartMetrics?.shift_id, resolveShiftTimes]);
68576
+ const resolvedShiftBreaks = useMemo(
68577
+ () => shiftConfig?.shifts?.find((shift) => shift.shiftId === chartMetrics?.shift_id)?.breaks || [],
68578
+ [shiftConfig?.shifts, chartMetrics?.shift_id]
68579
+ );
68312
68580
  const lineSkuBreakdown = useMemo(
68313
68581
  () => resolvedLineInfo?.metrics.sku_breakdown ?? [],
68314
68582
  [resolvedLineInfo]
@@ -68442,7 +68710,8 @@ var KPIDetailView = ({
68442
68710
  shiftEnd: null,
68443
68711
  shiftDate: null,
68444
68712
  timezone: configuredTimezone,
68445
- elapsedMinutes: null
68713
+ elapsedMinutes: null,
68714
+ shiftBreaks: resolvedShiftBreaks
68446
68715
  });
68447
68716
  }
68448
68717
  return buildUptimeSeries({
@@ -68451,9 +68720,10 @@ var KPIDetailView = ({
68451
68720
  shiftEnd: resolvedShiftTimes.end,
68452
68721
  shiftDate: chartMetrics.date,
68453
68722
  timezone: configuredTimezone,
68454
- elapsedMinutes: elapsedShiftMinutes
68723
+ elapsedMinutes: elapsedShiftMinutes,
68724
+ shiftBreaks: resolvedShiftBreaks
68455
68725
  });
68456
- }, [chartMetrics, resolvedShiftTimes.start, resolvedShiftTimes.end, configuredTimezone, elapsedShiftMinutes]);
68726
+ }, [chartMetrics, resolvedShiftTimes.start, resolvedShiftTimes.end, configuredTimezone, elapsedShiftMinutes, resolvedShiftBreaks]);
68457
68727
  const lineUtilizationFromLine = useMemo(() => {
68458
68728
  const efficiencyValue = Number.isFinite(chartMetrics?.avg_efficiency) ? Number(chartMetrics?.avg_efficiency) : null;
68459
68729
  if (efficiencyValue !== null) return efficiencyValue;
@@ -68480,7 +68750,8 @@ var KPIDetailView = ({
68480
68750
  shiftEnd,
68481
68751
  shiftDate,
68482
68752
  timezone: configuredTimezone,
68483
- elapsedMinutes: workspaceElapsedMinutes
68753
+ elapsedMinutes: workspaceElapsedMinutes,
68754
+ shiftBreaks: resolvedShiftBreaks
68484
68755
  });
68485
68756
  let activeMinutes = uptimeSeries2.activeMinutes;
68486
68757
  let idleMinutes = uptimeSeries2.idleMinutes;
@@ -68492,7 +68763,12 @@ var KPIDetailView = ({
68492
68763
  );
68493
68764
  const idleTimeValue = workspace.idle_time;
68494
68765
  const hasIdleTimeValue = Number.isFinite(idleTimeValue);
68495
- const fallbackDuration = workspaceElapsedMinutes ?? shiftMinutes;
68766
+ const fallbackDuration = getBreakExcludedShiftMinutes({
68767
+ shiftStart,
68768
+ shiftEnd,
68769
+ elapsedMinutes: workspaceElapsedMinutes,
68770
+ shiftBreaks: resolvedShiftBreaks
68771
+ }) ?? workspaceElapsedMinutes ?? shiftMinutes;
68496
68772
  if (!hasIdleHourlyPayload && hasIdleTimeValue && idleTimeValue > 0 && fallbackDuration > 0) {
68497
68773
  const idleSeconds = Number(idleTimeValue);
68498
68774
  const idleFromAggregate = Math.min(Math.max(idleSeconds / 60, 0), fallbackDuration);
@@ -68522,7 +68798,8 @@ var KPIDetailView = ({
68522
68798
  resolvedShiftTimes.end,
68523
68799
  chartMetrics?.date,
68524
68800
  configuredTimezone,
68525
- isCurrentShiftView
68801
+ isCurrentShiftView,
68802
+ resolvedShiftBreaks
68526
68803
  ]);
68527
68804
  const lineUptimeStats = useMemo(() => {
68528
68805
  if (!isUptimeMode) {
@@ -69209,6 +69486,7 @@ var KPIDetailView = ({
69209
69486
  shiftDate: chartMetrics?.date,
69210
69487
  timezone: configuredTimezone,
69211
69488
  elapsedMinutes: elapsedShiftMinutes,
69489
+ shiftBreaks: resolvedShiftBreaks,
69212
69490
  urlDate,
69213
69491
  urlShift,
69214
69492
  navigate
@@ -69323,6 +69601,7 @@ var KPIDetailView = ({
69323
69601
  shiftDate: chartMetrics?.date,
69324
69602
  timezone: configuredTimezone,
69325
69603
  elapsedMinutes: elapsedShiftMinutes,
69604
+ shiftBreaks: resolvedShiftBreaks,
69326
69605
  urlDate,
69327
69606
  urlShift,
69328
69607
  navigate
@@ -76808,6 +77087,10 @@ var WorkspaceDetailView = ({
76808
77087
  timezone
76809
77088
  });
76810
77089
  }, [isCurrentShiftView, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone]);
77090
+ const workspaceShiftBreaks = useMemo(
77091
+ () => shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace?.shift_id)?.breaks || [],
77092
+ [shiftConfig?.shifts, workspace?.shift_id]
77093
+ );
76811
77094
  const uptimeSeries = useMemo(
76812
77095
  () => buildUptimeSeries({
76813
77096
  idleTimeHourly: workspace?.idle_time_hourly,
@@ -76815,17 +77098,26 @@ var WorkspaceDetailView = ({
76815
77098
  shiftEnd: workspace?.shift_end,
76816
77099
  shiftDate: idleClipDate,
76817
77100
  timezone,
76818
- elapsedMinutes: elapsedShiftMinutes
77101
+ elapsedMinutes: elapsedShiftMinutes,
77102
+ shiftBreaks: workspaceShiftBreaks
76819
77103
  }),
76820
- [workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone, elapsedShiftMinutes]
77104
+ [workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone, elapsedShiftMinutes, workspaceShiftBreaks]
76821
77105
  );
76822
77106
  const uptimePieData = useMemo(() => {
76823
77107
  if (!isUptimeMode) return [];
76824
77108
  let activeMinutes = uptimeSeries.activeMinutes;
76825
77109
  let idleMinutes = uptimeSeries.idleMinutes;
76826
77110
  let totalMinutes = uptimeSeries.availableMinutes;
76827
- const fallbackDuration = elapsedShiftMinutes ?? shiftDurationMinutes;
76828
- if (!uptimeSeries.hasData && fallbackDuration !== null && fallbackDuration !== void 0) {
77111
+ const fallbackDuration = getBreakExcludedShiftMinutes({
77112
+ shiftStart: workspace?.shift_start,
77113
+ shiftEnd: workspace?.shift_end,
77114
+ elapsedMinutes: elapsedShiftMinutes,
77115
+ shiftBreaks: workspaceShiftBreaks
77116
+ }) ?? elapsedShiftMinutes ?? shiftDurationMinutes;
77117
+ const hasIdleHourlyPayload = Boolean(
77118
+ workspace?.idle_time_hourly && typeof workspace.idle_time_hourly === "object" && Object.keys(workspace.idle_time_hourly).length > 0
77119
+ );
77120
+ if (!uptimeSeries.hasData && !hasIdleHourlyPayload && fallbackDuration !== null && fallbackDuration !== void 0) {
76829
77121
  const idleSeconds = Number(workspace?.idle_time || 0);
76830
77122
  const idleFromAggregate = Math.min(Math.max(Math.round(idleSeconds / 60), 0), fallbackDuration);
76831
77123
  const activeFromAggregate = Math.max(fallbackDuration - idleFromAggregate, 0);
@@ -76838,7 +77130,7 @@ var WorkspaceDetailView = ({
76838
77130
  { name: "Productive", value: activeMinutes },
76839
77131
  { name: "Idle", value: idleMinutes }
76840
77132
  ];
76841
- }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time]);
77133
+ }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time, workspace?.shift_start, workspace?.shift_end, workspaceShiftBreaks]);
76842
77134
  const overviewTabLabel = isUptimeMode ? "Utilization" : "Efficiency";
76843
77135
  const idleClipFetchEnabled = Boolean(
76844
77136
  workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && isOutputLayout
@@ -77317,7 +77609,8 @@ var WorkspaceDetailView = ({
77317
77609
  shiftEnd: workspace.shift_end,
77318
77610
  shiftDate: idleClipDate,
77319
77611
  timezone,
77320
- elapsedMinutes: elapsedShiftMinutes
77612
+ elapsedMinutes: elapsedShiftMinutes,
77613
+ shiftBreaks: workspaceShiftBreaks
77321
77614
  }
77322
77615
  ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsx(
77323
77616
  CycleTimeOverTimeChart,
@@ -77470,7 +77763,8 @@ var WorkspaceDetailView = ({
77470
77763
  shiftEnd: workspace.shift_end,
77471
77764
  shiftDate: idleClipDate,
77472
77765
  timezone,
77473
- elapsedMinutes: elapsedShiftMinutes
77766
+ elapsedMinutes: elapsedShiftMinutes,
77767
+ shiftBreaks: workspaceShiftBreaks
77474
77768
  }
77475
77769
  ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsx(
77476
77770
  CycleTimeOverTimeChart,