@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.js CHANGED
@@ -14615,6 +14615,32 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
14615
14615
  }
14616
14616
  }
14617
14617
 
14618
+ // src/lib/utils/qaGreenStreakParams.ts
14619
+ var QA_GREEN_STREAK_QUERY_PARAM_NAMES = [
14620
+ "qa_green_streak_started_at",
14621
+ "qa_green_streak_elapsed_seconds",
14622
+ "qa_green_streak_status"
14623
+ ];
14624
+ var isTruthyEnv = (value) => ["1", "true", "yes", "on"].includes(String(value || "").trim().toLowerCase());
14625
+ var isQaGreenStreakForwardingEnabled = () => isTruthyEnv(process.env.NEXT_PUBLIC_DASHBOARD_QA_GREEN_STREAK_OVERRIDE_ENABLED);
14626
+ var appendQaGreenStreakSearchParams = (targetParams) => {
14627
+ if (!isQaGreenStreakForwardingEnabled() || typeof window === "undefined") {
14628
+ return;
14629
+ }
14630
+ const sourceParams = new URLSearchParams(window.location.search);
14631
+ QA_GREEN_STREAK_QUERY_PARAM_NAMES.forEach((paramName) => {
14632
+ const value = sourceParams.get(paramName);
14633
+ if (value !== null) {
14634
+ targetParams.set(paramName, value);
14635
+ }
14636
+ });
14637
+ };
14638
+ var getQaGreenStreakQueryString = () => {
14639
+ const params = new URLSearchParams();
14640
+ appendQaGreenStreakSearchParams(params);
14641
+ return params.toString();
14642
+ };
14643
+
14618
14644
  // src/lib/utils/monitorWorkspaceMetrics.ts
14619
14645
  var sortWorkspaceMetrics = (left, right) => {
14620
14646
  if (left.line_id !== right.line_id) {
@@ -14961,10 +14987,12 @@ var useDashboardMetrics = ({
14961
14987
  let idleTimeVlmByLine = {};
14962
14988
  let efficiencyLegend;
14963
14989
  const forceParam = force ? "&force_refresh=true" : "";
14990
+ const qaGreenStreakParamString = getQaGreenStreakQueryString();
14991
+ const qaGreenStreakParams = qaGreenStreakParamString ? `&${qaGreenStreakParamString}` : "";
14964
14992
  const buildMetricsEndpoint = (params) => {
14965
14993
  const lineIdsParam = isFactory ? `line_ids=${params.groupLineIds.join(",")}` : `line_id=${params.groupLineIds[0]}`;
14966
14994
  const blueComparisonParam = effectiveBlueComparisonLineIds.length ? `&blue_comparison_line_ids=${effectiveBlueComparisonLineIds.join(",")}` : "";
14967
- return `/api/dashboard/metrics?${lineIdsParam}${blueComparisonParam}&date=${params.date}&shift_id=${params.shiftId}&company_id=${companyId}${forceParam}`;
14995
+ return `/api/dashboard/metrics?${lineIdsParam}${blueComparisonParam}&date=${params.date}&shift_id=${params.shiftId}&company_id=${companyId}${forceParam}${qaGreenStreakParams}`;
14968
14996
  };
14969
14997
  if (usesShiftGroups) {
14970
14998
  logDebug("[useDashboardMetrics] Factory view shift groups fetch:", {
@@ -36118,6 +36146,37 @@ var interpretIdleValue = (value) => {
36118
36146
  if (value === "x" || value === null || value === void 0) return "unknown";
36119
36147
  return "unknown";
36120
36148
  };
36149
+ var timeToMinutes = (value) => {
36150
+ const parsed = parseTime(value);
36151
+ if (!parsed) return null;
36152
+ return parsed.hour * 60 + parsed.minute;
36153
+ };
36154
+ var normalizeBreaksOnShiftTimeline2 = (shiftStart, shiftBreaks) => {
36155
+ const shiftStartMinutes = timeToMinutes(shiftStart);
36156
+ if (shiftStartMinutes === null || !Array.isArray(shiftBreaks)) {
36157
+ return [];
36158
+ }
36159
+ return shiftBreaks.flatMap((entry) => {
36160
+ const startRaw = timeToMinutes(entry?.startTime ?? entry?.start);
36161
+ const endRaw = timeToMinutes(entry?.endTime ?? entry?.end);
36162
+ if (startRaw === null || endRaw === null) return [];
36163
+ let start = startRaw;
36164
+ let end = endRaw;
36165
+ if (end <= start) {
36166
+ end += 24 * 60;
36167
+ }
36168
+ if (start < shiftStartMinutes) {
36169
+ start += 24 * 60;
36170
+ end += 24 * 60;
36171
+ }
36172
+ return [{ start, end }];
36173
+ });
36174
+ };
36175
+ var isBreakMinute = (minuteIndex, shiftStartMinutes, normalizedBreaks) => {
36176
+ if (!normalizedBreaks.length) return false;
36177
+ const absoluteMinute = shiftStartMinutes + minuteIndex;
36178
+ return normalizedBreaks.some((entry) => entry.start <= absoluteMinute && absoluteMinute < entry.end);
36179
+ };
36121
36180
  var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
36122
36181
  const start = parseTime(shiftStart);
36123
36182
  const end = parseTime(shiftEnd);
@@ -36128,6 +36187,29 @@ var getShiftDurationMinutes = (shiftStart, shiftEnd) => {
36128
36187
  }
36129
36188
  return duration > 0 ? duration : null;
36130
36189
  };
36190
+ var getBreakExcludedShiftMinutes = ({
36191
+ shiftStart,
36192
+ shiftEnd,
36193
+ elapsedMinutes,
36194
+ shiftBreaks
36195
+ }) => {
36196
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
36197
+ if (shiftMinutes === null) return null;
36198
+ const elapsedLimit = Number.isFinite(elapsedMinutes) ? Math.min(Math.max(Math.floor(elapsedMinutes ?? 0), 0), shiftMinutes) : shiftMinutes;
36199
+ if (elapsedLimit <= 0) return 0;
36200
+ const startTime = parseTime(shiftStart);
36201
+ if (!startTime) return elapsedLimit;
36202
+ const shiftStartMinutes = startTime.hour * 60 + startTime.minute;
36203
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline2(shiftStart, shiftBreaks);
36204
+ if (!normalizedBreaks.length) return elapsedLimit;
36205
+ let workingMinutes = 0;
36206
+ for (let minuteIndex = 0; minuteIndex < elapsedLimit; minuteIndex += 1) {
36207
+ if (!isBreakMinute(minuteIndex, shiftStartMinutes, normalizedBreaks)) {
36208
+ workingMinutes += 1;
36209
+ }
36210
+ }
36211
+ return workingMinutes;
36212
+ };
36131
36213
  var getShiftElapsedMinutes = ({
36132
36214
  shiftStart,
36133
36215
  shiftEnd,
@@ -36192,7 +36274,8 @@ var buildUptimeSeries = ({
36192
36274
  shiftEnd,
36193
36275
  shiftDate,
36194
36276
  timezone,
36195
- elapsedMinutes
36277
+ elapsedMinutes,
36278
+ shiftBreaks
36196
36279
  }) => {
36197
36280
  const normalizedIdle = normalizeIdleTimeHourly(idleTimeHourly || {});
36198
36281
  const hasIdleData = Object.keys(normalizedIdle).length > 0;
@@ -36247,6 +36330,8 @@ var buildUptimeSeries = ({
36247
36330
  const points = [];
36248
36331
  let activeMinutes = 0;
36249
36332
  let idleMinutes = 0;
36333
+ const shiftStartMinutes = startTime.hour * 60 + startTime.minute;
36334
+ const normalizedBreaks = normalizeBreaksOnShiftTimeline2(shiftStart, shiftBreaks);
36250
36335
  for (let minuteIndex = 0; minuteIndex < shiftMinutes; minuteIndex += 1) {
36251
36336
  const minuteDate = dateFns.addMinutes(shiftStartDate, minuteIndex);
36252
36337
  const timeLabel = dateFnsTz.formatInTimeZone(minuteDate, timezone, "h:mm a");
@@ -36259,6 +36344,15 @@ var buildUptimeSeries = ({
36259
36344
  });
36260
36345
  continue;
36261
36346
  }
36347
+ if (isBreakMinute(minuteIndex, shiftStartMinutes, normalizedBreaks)) {
36348
+ points.push({
36349
+ minuteIndex,
36350
+ timeLabel,
36351
+ uptime: null,
36352
+ status: "break"
36353
+ });
36354
+ continue;
36355
+ }
36262
36356
  const hourKey = dateFnsTz.formatInTimeZone(minuteDate, timezone, "H");
36263
36357
  const minuteKey = Number.parseInt(dateFnsTz.formatInTimeZone(minuteDate, timezone, "m"), 10);
36264
36358
  const hourBucket = normalizedIdle[hourKey] || [];
@@ -37820,12 +37914,10 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
37820
37914
  const startedAt = workspace.video_grid_green_streak_started_at;
37821
37915
  const anchorAt = workspace.video_grid_green_streak_anchor_at;
37822
37916
  const streakMinutes = workspace.video_grid_green_streak_minutes;
37823
- const computedAt = workspace.recent_flow_computed_at;
37824
37917
  const startedAtMs = parseTimestampMs(startedAt);
37825
37918
  const anchorAtMs = parseTimestampMs(anchorAt);
37826
- const computedAtMs = typeof computedAt === "string" && computedAt.trim() ? parseTimestampMs(computedAt) : Number.NaN;
37827
37919
  const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs2);
37828
- const tickOriginAtMs = Number.isFinite(computedAtMs) ? computedAtMs : anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
37920
+ const tickOriginAtMs = anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
37829
37921
  if (workspace.video_grid_green_streak_active === true && isFiniteNumber3(streakMinutes) && streakMinutes > 0 && startedAt && anchorAt && Number.isFinite(startedAtMs) && isFresh) {
37830
37922
  return {
37831
37923
  startedAt,
@@ -39421,6 +39513,7 @@ var HourlyUptimeChartComponent = ({
39421
39513
  shiftDate,
39422
39514
  timezone,
39423
39515
  elapsedMinutes,
39516
+ shiftBreaks,
39424
39517
  className = ""
39425
39518
  }) => {
39426
39519
  const containerRef = React144__namespace.default.useRef(null);
@@ -39432,8 +39525,9 @@ var HourlyUptimeChartComponent = ({
39432
39525
  shiftEnd,
39433
39526
  shiftDate,
39434
39527
  timezone,
39435
- elapsedMinutes
39436
- }), [idleTimeHourly, shiftStart, shiftEnd, shiftDate, timezone, elapsedMinutes]);
39528
+ elapsedMinutes,
39529
+ shiftBreaks
39530
+ }), [idleTimeHourly, shiftStart, shiftEnd, shiftDate, timezone, elapsedMinutes, shiftBreaks]);
39437
39531
  const hasAggregateData = Boolean(hourlyAggregates && hourlyAggregates.length > 0);
39438
39532
  const shiftStartTime = React144__namespace.default.useMemo(
39439
39533
  () => getTimeFromTimeString(shiftStart),
@@ -51625,7 +51719,8 @@ var LineMonthlyPdfGenerator = ({
51625
51719
  };
51626
51720
  var LineWhatsAppShareButton = ({
51627
51721
  lineInfo,
51628
- className
51722
+ className,
51723
+ shiftBreaks = []
51629
51724
  }) => {
51630
51725
  const handleShare = () => {
51631
51726
  trackCoreEvent("Line WhatsApp Share Clicked", {
@@ -51639,7 +51734,8 @@ var LineWhatsAppShareButton = ({
51639
51734
  shiftStart: lineInfo.metrics.shift_start,
51640
51735
  shiftEnd: lineInfo.metrics.shift_end,
51641
51736
  shiftDate: lineInfo.date,
51642
- timezone: "Asia/Kolkata"
51737
+ timezone: "Asia/Kolkata",
51738
+ shiftBreaks
51643
51739
  }) : null;
51644
51740
  const efficiencyValue = Number.isFinite(lineInfo.metrics.avg_efficiency) ? Number(lineInfo.metrics.avg_efficiency) : null;
51645
51741
  const utilization = efficiencyValue !== null ? efficiencyValue : uptimeSeries && uptimeSeries.availableMinutes > 0 ? uptimeSeries.activeMinutes / uptimeSeries.availableMinutes * 100 : 0;
@@ -51808,16 +51904,24 @@ var LinePdfGenerator = ({
51808
51904
  shiftStart,
51809
51905
  shiftEnd,
51810
51906
  shiftDate,
51811
- timezone: effectiveUptimeTimezone
51907
+ timezone: effectiveUptimeTimezone,
51908
+ shiftBreaks
51812
51909
  });
51813
51910
  let activeMinutes = uptimeSeries.activeMinutes;
51814
51911
  let idleMinutes = uptimeSeries.idleMinutes;
51815
51912
  let availableMinutes = uptimeSeries.availableMinutes;
51816
51913
  let hasData = uptimeSeries.hasData;
51817
- if (!hasData) {
51914
+ const hasIdleHourlyPayload = Boolean(
51915
+ workspace.idle_time_hourly && typeof workspace.idle_time_hourly === "object" && Object.keys(workspace.idle_time_hourly).length > 0
51916
+ );
51917
+ if (!hasData && !hasIdleHourlyPayload) {
51818
51918
  const idleTimeValue = workspace.idle_time;
51819
51919
  const hasIdleTimeValue = Number.isFinite(idleTimeValue);
51820
- const fallbackDuration = shiftDurationMinutes;
51920
+ const fallbackDuration = getBreakExcludedShiftMinutes({
51921
+ shiftStart,
51922
+ shiftEnd,
51923
+ shiftBreaks
51924
+ }) ?? shiftDurationMinutes;
51821
51925
  if (hasIdleTimeValue && idleTimeValue > 0 && fallbackDuration > 0) {
51822
51926
  const idleFromAggregate = Math.min(Math.max(Number(idleTimeValue) / 60, 0), fallbackDuration);
51823
51927
  idleMinutes = idleFromAggregate;
@@ -51889,7 +51993,8 @@ var LinePdfGenerator = ({
51889
51993
  shiftStart: lineShiftStart,
51890
51994
  shiftEnd: lineShiftEnd,
51891
51995
  shiftDate: lineInfo.date,
51892
- timezone: effectiveUptimeTimezone
51996
+ timezone: effectiveUptimeTimezone,
51997
+ shiftBreaks
51893
51998
  });
51894
51999
  hourlyData = buildHourlyFromSeries(lineUptimeSeries);
51895
52000
  }
@@ -53828,7 +53933,8 @@ var WorkspaceMonthlyHistory = ({
53828
53933
  };
53829
53934
  var WorkspaceWhatsAppShareButton = ({
53830
53935
  workspace,
53831
- className
53936
+ className,
53937
+ shiftBreaks = []
53832
53938
  }) => {
53833
53939
  const handleShare = () => {
53834
53940
  trackCoreEvent("Workspace WhatsApp Share Clicked", {
@@ -53844,7 +53950,11 @@ var WorkspaceWhatsAppShareButton = ({
53844
53950
  timeZone: "Asia/Kolkata"
53845
53951
  });
53846
53952
  const isUptimeMode = workspace.monitoring_mode === "uptime";
53847
- const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
53953
+ const shiftMinutes = getBreakExcludedShiftMinutes({
53954
+ shiftStart: workspace.shift_start,
53955
+ shiftEnd: workspace.shift_end,
53956
+ shiftBreaks
53957
+ }) ?? getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
53848
53958
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
53849
53959
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
53850
53960
  const clampedIdleSeconds = shiftSeconds > 0 ? Math.min(idleSeconds, shiftSeconds) : idleSeconds;
@@ -53934,7 +54044,11 @@ var WorkspacePdfGenerator = ({
53934
54044
  try {
53935
54045
  const isUptimeMode = workspace.monitoring_mode === "uptime";
53936
54046
  const isAssemblyCycleMode = !isUptimeMode && shouldUseAssemblyCycleTimeLayout(workspace);
53937
- const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
54047
+ const shiftMinutes = getBreakExcludedShiftMinutes({
54048
+ shiftStart: workspace.shift_start,
54049
+ shiftEnd: workspace.shift_end,
54050
+ shiftBreaks
54051
+ }) ?? getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
53938
54052
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
53939
54053
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
53940
54054
  const clampedIdleSeconds = shiftSeconds > 0 ? Math.min(idleSeconds, shiftSeconds) : idleSeconds;
@@ -54104,7 +54218,8 @@ var WorkspacePdfGenerator = ({
54104
54218
  shiftStart: workspace.shift_start,
54105
54219
  shiftEnd: workspace.shift_end,
54106
54220
  shiftDate: workspace.date,
54107
- timezone: reportTimezone
54221
+ timezone: reportTimezone,
54222
+ shiftBreaks
54108
54223
  }) : null;
54109
54224
  const hourlyUptime = uptimeSeries?.points?.length ? Array.from({ length: Math.ceil(uptimeSeries.shiftMinutes / 60) }, (_, index) => {
54110
54225
  const start = index * 60;
@@ -63964,6 +64079,8 @@ var HelpView = ({
63964
64079
  };
63965
64080
  var AuthenticatedHelpView = withAuth(HelpView);
63966
64081
  var HelpView_default = HelpView;
64082
+ var REALTIME_REFRESH_DEBOUNCE_MS2 = 1500;
64083
+ var REALTIME_REFRESH_MIN_INTERVAL_MS2 = 5e3;
63967
64084
  var transformActiveBreaks = (activeBreaksByLine) => {
63968
64085
  if (!activeBreaksByLine) return [];
63969
64086
  return Object.values(activeBreaksByLine).flat().map((item) => ({
@@ -64002,6 +64119,17 @@ var normalizeMetadata = (metadata) => ({
64002
64119
  cacheStatus: metadata?.cache_status,
64003
64120
  warnings: metadata?.warnings ?? []
64004
64121
  });
64122
+ var createResolvedScopeLookup = (resolvedScope, workspaces, blueComparisonWorkspaces) => {
64123
+ const lookup = /* @__PURE__ */ new Set();
64124
+ const addEntry = (lineId, date, shiftId) => {
64125
+ if (!lineId || !date || shiftId === void 0 || shiftId === null) return;
64126
+ lookup.add(`${lineId}|${date}|${shiftId}`);
64127
+ };
64128
+ resolvedScope.forEach((entry) => addEntry(entry.line_id, entry.date, entry.shift_id));
64129
+ workspaces.forEach((workspace) => addEntry(workspace.line_id, workspace.date, workspace.shift_id));
64130
+ blueComparisonWorkspaces.forEach((workspace) => addEntry(workspace.line_id, workspace.date, workspace.shift_id));
64131
+ return lookup;
64132
+ };
64005
64133
  var useLiveMonitorBootstrap = ({
64006
64134
  lineIds,
64007
64135
  blueComparisonLineIds,
@@ -64028,20 +64156,34 @@ var useLiveMonitorBootstrap = ({
64028
64156
  () => Array.from(new Set(rawBlueComparisonLineIdsKey ? rawBlueComparisonLineIdsKey.split(",") : [])),
64029
64157
  [rawBlueComparisonLineIdsKey]
64030
64158
  );
64159
+ const realtimeLineIds = React144.useMemo(
64160
+ () => Array.from(new Set([...normalizedLineIds, ...normalizedBlueComparisonLineIds].filter(Boolean))).sort(),
64161
+ [normalizedLineIds, normalizedBlueComparisonLineIds]
64162
+ );
64031
64163
  const requestKey = React144.useMemo(
64032
64164
  () => `${normalizedLineIds.slice().sort().join(",")}|blue:${normalizedBlueComparisonLineIds.slice().sort().join(",")}`,
64033
64165
  [normalizedLineIds, normalizedBlueComparisonLineIds]
64034
64166
  );
64167
+ const realtimeLineIdsKey = React144.useMemo(() => realtimeLineIds.join(","), [realtimeLineIds]);
64168
+ const companySpecificMetricsTable = React144.useMemo(
64169
+ () => getCompanyMetricsTableName(resolvedCompanyId, "performance_metrics"),
64170
+ [resolvedCompanyId]
64171
+ );
64035
64172
  const [state, setState] = React144.useState(() => createEmptyState());
64036
64173
  const [rawState, setRawState] = React144.useState(null);
64037
64174
  const [isLoading, setIsLoading] = React144.useState(false);
64038
64175
  const [error, setError] = React144.useState(null);
64039
64176
  const activeRequestIdRef = React144.useRef(0);
64177
+ const isFetchingRef = React144.useRef(false);
64178
+ const pendingRealtimeRefreshRef = React144.useRef(false);
64179
+ const realtimeRefreshTimerRef = React144.useRef(null);
64180
+ const lastRealtimeRefreshStartedAtRef = React144.useRef(0);
64040
64181
  const fetchBootstrap = React144.useCallback(async (force = false) => {
64041
64182
  if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
64042
64183
  return;
64043
64184
  }
64044
64185
  const requestId = ++activeRequestIdRef.current;
64186
+ isFetchingRef.current = true;
64045
64187
  setIsLoading(true);
64046
64188
  setError(null);
64047
64189
  try {
@@ -64054,6 +64196,7 @@ var useLiveMonitorBootstrap = ({
64054
64196
  if (force) {
64055
64197
  searchParams.set("force_refresh", "true");
64056
64198
  }
64199
+ appendQaGreenStreakSearchParams(searchParams);
64057
64200
  const response = await fetchBackendJson(
64058
64201
  supabase,
64059
64202
  `/api/dashboard/monitor-bootstrap?${searchParams.toString()}`,
@@ -64091,6 +64234,7 @@ var useLiveMonitorBootstrap = ({
64091
64234
  } finally {
64092
64235
  if (requestId === activeRequestIdRef.current) {
64093
64236
  setIsLoading(false);
64237
+ isFetchingRef.current = false;
64094
64238
  }
64095
64239
  }
64096
64240
  }, [
@@ -64101,6 +64245,52 @@ var useLiveMonitorBootstrap = ({
64101
64245
  normalizedBlueComparisonLineIds,
64102
64246
  requestKey
64103
64247
  ]);
64248
+ const fetchBootstrapRef = React144.useRef(fetchBootstrap);
64249
+ React144.useEffect(() => {
64250
+ fetchBootstrapRef.current = fetchBootstrap;
64251
+ }, [fetchBootstrap]);
64252
+ const clearRealtimeRefreshTimer = React144.useCallback(() => {
64253
+ if (realtimeRefreshTimerRef.current !== null) {
64254
+ window.clearTimeout(realtimeRefreshTimerRef.current);
64255
+ realtimeRefreshTimerRef.current = null;
64256
+ }
64257
+ }, []);
64258
+ const scheduleRealtimeRefresh = React144.useCallback(() => {
64259
+ if (!enabled || !supabase || realtimeRefreshTimerRef.current !== null) {
64260
+ return;
64261
+ }
64262
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
64263
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS2 - elapsedSinceLastRealtimeRefresh);
64264
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS2, minIntervalRemaining);
64265
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
64266
+ realtimeRefreshTimerRef.current = null;
64267
+ if (!pendingRealtimeRefreshRef.current) {
64268
+ return;
64269
+ }
64270
+ if (isFetchingRef.current) {
64271
+ scheduleRealtimeRefresh();
64272
+ return;
64273
+ }
64274
+ pendingRealtimeRefreshRef.current = false;
64275
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
64276
+ void fetchBootstrapRef.current(true);
64277
+ }, nextDelay);
64278
+ }, [enabled, supabase]);
64279
+ const queueRealtimeRefresh = React144.useCallback(() => {
64280
+ if (!enabled || !supabase) {
64281
+ pendingRealtimeRefreshRef.current = false;
64282
+ clearRealtimeRefreshTimer();
64283
+ return;
64284
+ }
64285
+ pendingRealtimeRefreshRef.current = true;
64286
+ scheduleRealtimeRefresh();
64287
+ }, [clearRealtimeRefreshTimer, enabled, scheduleRealtimeRefresh, supabase]);
64288
+ React144.useEffect(() => {
64289
+ return () => {
64290
+ pendingRealtimeRefreshRef.current = false;
64291
+ clearRealtimeRefreshTimer();
64292
+ };
64293
+ }, [clearRealtimeRefreshTimer]);
64104
64294
  React144.useEffect(() => {
64105
64295
  if (!enabled || !rawState || !resolvedCompanyId) {
64106
64296
  return;
@@ -64179,6 +64369,83 @@ var useLiveMonitorBootstrap = ({
64179
64369
  }
64180
64370
  void fetchBootstrap(false);
64181
64371
  }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
64372
+ const realtimeScopeKey = React144.useMemo(() => Array.from(createResolvedScopeLookup(
64373
+ state.resolvedScope,
64374
+ state.workspaceMetrics,
64375
+ state.blueComparisonWorkspaceMetrics
64376
+ )).sort().join("||"), [state.resolvedScope, state.workspaceMetrics, state.blueComparisonWorkspaceMetrics]);
64377
+ React144.useEffect(() => {
64378
+ if (!enabled || !resolvedCompanyId || !realtimeLineIdsKey || !supabase) {
64379
+ return void 0;
64380
+ }
64381
+ const scopeLookup = new Set(realtimeScopeKey ? realtimeScopeKey.split("||") : []);
64382
+ if (scopeLookup.size === 0) {
64383
+ return void 0;
64384
+ }
64385
+ const realtimeLineIdsForFilter = realtimeLineIdsKey.split(",").filter(Boolean);
64386
+ if (realtimeLineIdsForFilter.length === 0) {
64387
+ return void 0;
64388
+ }
64389
+ const lineIdFilter = `line_id=in.(${realtimeLineIdsForFilter.join(",")})`;
64390
+ const channels = [];
64391
+ const shouldRefreshForPayload = (payload) => {
64392
+ const payloadData = payload.new || payload.old;
64393
+ return scopeLookup.has(`${payloadData?.line_id}|${payloadData?.date}|${payloadData?.shift_id}`);
64394
+ };
64395
+ const createSubscription = (table, channelNameBase) => {
64396
+ const channelName = `${channelNameBase}-${state.scopeKey || requestKey}`.replace(/[^a-zA-Z0-9_-]/g, "");
64397
+ const channel = supabase.channel(channelName).on(
64398
+ "postgres_changes",
64399
+ { event: "*", schema: "public", table, filter: lineIdFilter },
64400
+ (payload) => {
64401
+ if (shouldRefreshForPayload(payload)) {
64402
+ queueRealtimeRefresh();
64403
+ }
64404
+ }
64405
+ ).subscribe();
64406
+ channels.push(channel);
64407
+ };
64408
+ createSubscription(companySpecificMetricsTable, "monitor-bootstrap-ws");
64409
+ createSubscription("line_metrics", "monitor-bootstrap-lm");
64410
+ createSubscription("wip_buffer_metrics", "monitor-bootstrap-wip-metrics");
64411
+ createSubscription("wip_buffer_alerts", "monitor-bootstrap-wip-alerts");
64412
+ return () => {
64413
+ channels.forEach((channel) => {
64414
+ supabase.removeChannel(channel);
64415
+ });
64416
+ };
64417
+ }, [
64418
+ enabled,
64419
+ resolvedCompanyId,
64420
+ realtimeLineIdsKey,
64421
+ supabase,
64422
+ realtimeScopeKey,
64423
+ state.scopeKey,
64424
+ requestKey,
64425
+ companySpecificMetricsTable,
64426
+ queueRealtimeRefresh
64427
+ ]);
64428
+ React144.useEffect(() => {
64429
+ if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
64430
+ return void 0;
64431
+ }
64432
+ const forceRefreshOnResume = () => {
64433
+ void fetchBootstrapRef.current(true);
64434
+ };
64435
+ const handleVisibilityChange = () => {
64436
+ if (document.visibilityState === "visible") {
64437
+ forceRefreshOnResume();
64438
+ }
64439
+ };
64440
+ document.addEventListener("visibilitychange", handleVisibilityChange);
64441
+ window.addEventListener("focus", forceRefreshOnResume);
64442
+ window.addEventListener("online", forceRefreshOnResume);
64443
+ return () => {
64444
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
64445
+ window.removeEventListener("focus", forceRefreshOnResume);
64446
+ window.removeEventListener("online", forceRefreshOnResume);
64447
+ };
64448
+ }, [enabled, normalizedLineIds.length, resolvedCompanyId, supabase]);
64182
64449
  React144.useEffect(() => {
64183
64450
  if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
64184
64451
  return void 0;
@@ -64933,7 +65200,6 @@ function HomeView({
64933
65200
  const allGreenMilestoneTrackingRef = React144.useRef({ identity: null, lastMilestoneSeconds: null });
64934
65201
  const allGreenStreakTimerBaselineRef = React144.useRef(null);
64935
65202
  const [showAllGreenCelebration, setShowAllGreenCelebration] = React144.useState(false);
64936
- const [allGreenCelebrationStartedAtMs, setAllGreenCelebrationStartedAtMs] = React144.useState(null);
64937
65203
  const [greenStreakMilestoneBanner, setGreenStreakMilestoneBanner] = React144.useState(null);
64938
65204
  const [allGreenStreakNowMs, setAllGreenStreakNowMs] = React144.useState(() => Date.now());
64939
65205
  const currentAllGreenStreakNowMs = Math.max(allGreenStreakNowMs, Date.now());
@@ -64996,13 +65262,11 @@ function HomeView({
64996
65262
  };
64997
65263
  }, [allGreenMilestoneIdentity, allGreenStreakDisplay, currentAllGreenStreakNowMs]);
64998
65264
  const allGreenCelebrationTimerText = React144.useMemo(() => {
64999
- if (!showAllGreenCelebration || allGreenCelebrationStartedAtMs === null) {
65265
+ if (!showAllGreenCelebration || !visibleAllGreenStreakDisplay) {
65000
65266
  return null;
65001
65267
  }
65002
- return formatAllGreenCelebrationTimer(
65003
- Math.max(0, Math.floor((currentAllGreenStreakNowMs - allGreenCelebrationStartedAtMs) / 1e3))
65004
- );
65005
- }, [allGreenCelebrationStartedAtMs, currentAllGreenStreakNowMs, showAllGreenCelebration]);
65268
+ return formatAllGreenCelebrationTimer(visibleAllGreenStreakDisplay.elapsedSeconds);
65269
+ }, [showAllGreenCelebration, visibleAllGreenStreakDisplay]);
65006
65270
  React144.useEffect(() => {
65007
65271
  const hasPossibleStreakTimer = showAllGreenCelebration || Boolean(allGreenStreakDisplay) || workspaceMetricsWithBreakState.some((workspace) => workspace.video_grid_green_streak_active === true);
65008
65272
  if (!hasPossibleStreakTimer) {
@@ -65021,17 +65285,14 @@ function HomeView({
65021
65285
  allGreenCelebrationTimerRef.current = null;
65022
65286
  }
65023
65287
  setShowAllGreenCelebration(false);
65024
- setAllGreenCelebrationStartedAtMs(null);
65025
65288
  }, []);
65026
65289
  const triggerAllGreenCelebration = React144.useCallback(() => {
65027
- setAllGreenCelebrationStartedAtMs(Date.now());
65028
65290
  setShowAllGreenCelebration(true);
65029
65291
  if (allGreenCelebrationTimerRef.current) {
65030
65292
  clearTimeout(allGreenCelebrationTimerRef.current);
65031
65293
  }
65032
65294
  allGreenCelebrationTimerRef.current = setTimeout(() => {
65033
65295
  setShowAllGreenCelebration(false);
65034
- setAllGreenCelebrationStartedAtMs(null);
65035
65296
  allGreenCelebrationTimerRef.current = null;
65036
65297
  }, ALL_GREEN_CELEBRATION_DURATION_MS);
65037
65298
  }, []);
@@ -67753,6 +68014,7 @@ var UptimeBottomSection = React144.memo(({
67753
68014
  shiftDate,
67754
68015
  timezone,
67755
68016
  elapsedMinutes,
68017
+ shiftBreaks,
67756
68018
  urlDate,
67757
68019
  urlShift,
67758
68020
  navigate
@@ -67840,7 +68102,8 @@ var UptimeBottomSection = React144.memo(({
67840
68102
  shiftEnd,
67841
68103
  shiftDate,
67842
68104
  timezone,
67843
- elapsedMinutes
68105
+ elapsedMinutes,
68106
+ shiftBreaks
67844
68107
  }
67845
68108
  ) })
67846
68109
  ] })
@@ -68158,7 +68421,8 @@ var KPIDetailView = ({
68158
68421
  shiftStart: resolvedShiftStart || void 0,
68159
68422
  shiftEnd: resolvedShiftEnd || void 0,
68160
68423
  shiftDate: metric.date,
68161
- timezone: configuredTimezone
68424
+ timezone: configuredTimezone,
68425
+ shiftBreaks: shiftConfig?.shifts?.find((shift) => shift.shiftId === metric.shift_id)?.breaks || []
68162
68426
  }) : null;
68163
68427
  const idleTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metricIdleTimeSeconds ?? 0 : (uptimeSeries2?.idleMinutes || 0) * 60 : 0;
68164
68428
  const activeTimeSeconds = isUptimeMode ? hasBackendUptimeSeconds ? metricActiveTimeSeconds ?? 0 : (uptimeSeries2?.activeMinutes || 0) * 60 : 0;
@@ -68338,6 +68602,10 @@ var KPIDetailView = ({
68338
68602
  end: chartMetrics.shift_end || fallback.end
68339
68603
  };
68340
68604
  }, [chartMetrics?.shift_start, chartMetrics?.shift_end, chartMetrics?.shift_id, resolveShiftTimes]);
68605
+ const resolvedShiftBreaks = React144.useMemo(
68606
+ () => shiftConfig?.shifts?.find((shift) => shift.shiftId === chartMetrics?.shift_id)?.breaks || [],
68607
+ [shiftConfig?.shifts, chartMetrics?.shift_id]
68608
+ );
68341
68609
  const lineSkuBreakdown = React144.useMemo(
68342
68610
  () => resolvedLineInfo?.metrics.sku_breakdown ?? [],
68343
68611
  [resolvedLineInfo]
@@ -68471,7 +68739,8 @@ var KPIDetailView = ({
68471
68739
  shiftEnd: null,
68472
68740
  shiftDate: null,
68473
68741
  timezone: configuredTimezone,
68474
- elapsedMinutes: null
68742
+ elapsedMinutes: null,
68743
+ shiftBreaks: resolvedShiftBreaks
68475
68744
  });
68476
68745
  }
68477
68746
  return buildUptimeSeries({
@@ -68480,9 +68749,10 @@ var KPIDetailView = ({
68480
68749
  shiftEnd: resolvedShiftTimes.end,
68481
68750
  shiftDate: chartMetrics.date,
68482
68751
  timezone: configuredTimezone,
68483
- elapsedMinutes: elapsedShiftMinutes
68752
+ elapsedMinutes: elapsedShiftMinutes,
68753
+ shiftBreaks: resolvedShiftBreaks
68484
68754
  });
68485
- }, [chartMetrics, resolvedShiftTimes.start, resolvedShiftTimes.end, configuredTimezone, elapsedShiftMinutes]);
68755
+ }, [chartMetrics, resolvedShiftTimes.start, resolvedShiftTimes.end, configuredTimezone, elapsedShiftMinutes, resolvedShiftBreaks]);
68486
68756
  const lineUtilizationFromLine = React144.useMemo(() => {
68487
68757
  const efficiencyValue = Number.isFinite(chartMetrics?.avg_efficiency) ? Number(chartMetrics?.avg_efficiency) : null;
68488
68758
  if (efficiencyValue !== null) return efficiencyValue;
@@ -68509,7 +68779,8 @@ var KPIDetailView = ({
68509
68779
  shiftEnd,
68510
68780
  shiftDate,
68511
68781
  timezone: configuredTimezone,
68512
- elapsedMinutes: workspaceElapsedMinutes
68782
+ elapsedMinutes: workspaceElapsedMinutes,
68783
+ shiftBreaks: resolvedShiftBreaks
68513
68784
  });
68514
68785
  let activeMinutes = uptimeSeries2.activeMinutes;
68515
68786
  let idleMinutes = uptimeSeries2.idleMinutes;
@@ -68521,7 +68792,12 @@ var KPIDetailView = ({
68521
68792
  );
68522
68793
  const idleTimeValue = workspace.idle_time;
68523
68794
  const hasIdleTimeValue = Number.isFinite(idleTimeValue);
68524
- const fallbackDuration = workspaceElapsedMinutes ?? shiftMinutes;
68795
+ const fallbackDuration = getBreakExcludedShiftMinutes({
68796
+ shiftStart,
68797
+ shiftEnd,
68798
+ elapsedMinutes: workspaceElapsedMinutes,
68799
+ shiftBreaks: resolvedShiftBreaks
68800
+ }) ?? workspaceElapsedMinutes ?? shiftMinutes;
68525
68801
  if (!hasIdleHourlyPayload && hasIdleTimeValue && idleTimeValue > 0 && fallbackDuration > 0) {
68526
68802
  const idleSeconds = Number(idleTimeValue);
68527
68803
  const idleFromAggregate = Math.min(Math.max(idleSeconds / 60, 0), fallbackDuration);
@@ -68551,7 +68827,8 @@ var KPIDetailView = ({
68551
68827
  resolvedShiftTimes.end,
68552
68828
  chartMetrics?.date,
68553
68829
  configuredTimezone,
68554
- isCurrentShiftView
68830
+ isCurrentShiftView,
68831
+ resolvedShiftBreaks
68555
68832
  ]);
68556
68833
  const lineUptimeStats = React144.useMemo(() => {
68557
68834
  if (!isUptimeMode) {
@@ -69238,6 +69515,7 @@ var KPIDetailView = ({
69238
69515
  shiftDate: chartMetrics?.date,
69239
69516
  timezone: configuredTimezone,
69240
69517
  elapsedMinutes: elapsedShiftMinutes,
69518
+ shiftBreaks: resolvedShiftBreaks,
69241
69519
  urlDate,
69242
69520
  urlShift,
69243
69521
  navigate
@@ -69352,6 +69630,7 @@ var KPIDetailView = ({
69352
69630
  shiftDate: chartMetrics?.date,
69353
69631
  timezone: configuredTimezone,
69354
69632
  elapsedMinutes: elapsedShiftMinutes,
69633
+ shiftBreaks: resolvedShiftBreaks,
69355
69634
  urlDate,
69356
69635
  urlShift,
69357
69636
  navigate
@@ -76837,6 +77116,10 @@ var WorkspaceDetailView = ({
76837
77116
  timezone
76838
77117
  });
76839
77118
  }, [isCurrentShiftView, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone]);
77119
+ const workspaceShiftBreaks = React144.useMemo(
77120
+ () => shiftConfig?.shifts?.find((shift2) => shift2.shiftId === workspace?.shift_id)?.breaks || [],
77121
+ [shiftConfig?.shifts, workspace?.shift_id]
77122
+ );
76840
77123
  const uptimeSeries = React144.useMemo(
76841
77124
  () => buildUptimeSeries({
76842
77125
  idleTimeHourly: workspace?.idle_time_hourly,
@@ -76844,17 +77127,26 @@ var WorkspaceDetailView = ({
76844
77127
  shiftEnd: workspace?.shift_end,
76845
77128
  shiftDate: idleClipDate,
76846
77129
  timezone,
76847
- elapsedMinutes: elapsedShiftMinutes
77130
+ elapsedMinutes: elapsedShiftMinutes,
77131
+ shiftBreaks: workspaceShiftBreaks
76848
77132
  }),
76849
- [workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone, elapsedShiftMinutes]
77133
+ [workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end, idleClipDate, timezone, elapsedShiftMinutes, workspaceShiftBreaks]
76850
77134
  );
76851
77135
  const uptimePieData = React144.useMemo(() => {
76852
77136
  if (!isUptimeMode) return [];
76853
77137
  let activeMinutes = uptimeSeries.activeMinutes;
76854
77138
  let idleMinutes = uptimeSeries.idleMinutes;
76855
77139
  let totalMinutes = uptimeSeries.availableMinutes;
76856
- const fallbackDuration = elapsedShiftMinutes ?? shiftDurationMinutes;
76857
- if (!uptimeSeries.hasData && fallbackDuration !== null && fallbackDuration !== void 0) {
77140
+ const fallbackDuration = getBreakExcludedShiftMinutes({
77141
+ shiftStart: workspace?.shift_start,
77142
+ shiftEnd: workspace?.shift_end,
77143
+ elapsedMinutes: elapsedShiftMinutes,
77144
+ shiftBreaks: workspaceShiftBreaks
77145
+ }) ?? elapsedShiftMinutes ?? shiftDurationMinutes;
77146
+ const hasIdleHourlyPayload = Boolean(
77147
+ workspace?.idle_time_hourly && typeof workspace.idle_time_hourly === "object" && Object.keys(workspace.idle_time_hourly).length > 0
77148
+ );
77149
+ if (!uptimeSeries.hasData && !hasIdleHourlyPayload && fallbackDuration !== null && fallbackDuration !== void 0) {
76858
77150
  const idleSeconds = Number(workspace?.idle_time || 0);
76859
77151
  const idleFromAggregate = Math.min(Math.max(Math.round(idleSeconds / 60), 0), fallbackDuration);
76860
77152
  const activeFromAggregate = Math.max(fallbackDuration - idleFromAggregate, 0);
@@ -76867,7 +77159,7 @@ var WorkspaceDetailView = ({
76867
77159
  { name: "Productive", value: activeMinutes },
76868
77160
  { name: "Idle", value: idleMinutes }
76869
77161
  ];
76870
- }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time]);
77162
+ }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time, workspace?.shift_start, workspace?.shift_end, workspaceShiftBreaks]);
76871
77163
  const overviewTabLabel = isUptimeMode ? "Utilization" : "Efficiency";
76872
77164
  const idleClipFetchEnabled = Boolean(
76873
77165
  workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && isOutputLayout
@@ -77346,7 +77638,8 @@ var WorkspaceDetailView = ({
77346
77638
  shiftEnd: workspace.shift_end,
77347
77639
  shiftDate: idleClipDate,
77348
77640
  timezone,
77349
- elapsedMinutes: elapsedShiftMinutes
77641
+ elapsedMinutes: elapsedShiftMinutes,
77642
+ shiftBreaks: workspaceShiftBreaks
77350
77643
  }
77351
77644
  ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
77352
77645
  CycleTimeOverTimeChart,
@@ -77499,7 +77792,8 @@ var WorkspaceDetailView = ({
77499
77792
  shiftEnd: workspace.shift_end,
77500
77793
  shiftDate: idleClipDate,
77501
77794
  timezone,
77502
- elapsedMinutes: elapsedShiftMinutes
77795
+ elapsedMinutes: elapsedShiftMinutes,
77796
+ shiftBreaks: workspaceShiftBreaks
77503
77797
  }
77504
77798
  ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
77505
77799
  CycleTimeOverTimeChart,