@optifye/dashboard-core 6.12.23 → 6.12.25

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.
Files changed (3) hide show
  1. package/dist/index.js +185 -13
  2. package/dist/index.mjs +185 -13
  3. package/package.json +1 -1
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:", {
@@ -37820,12 +37848,10 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
37820
37848
  const startedAt = workspace.video_grid_green_streak_started_at;
37821
37849
  const anchorAt = workspace.video_grid_green_streak_anchor_at;
37822
37850
  const streakMinutes = workspace.video_grid_green_streak_minutes;
37823
- const computedAt = workspace.recent_flow_computed_at;
37824
37851
  const startedAtMs = parseTimestampMs(startedAt);
37825
37852
  const anchorAtMs = parseTimestampMs(anchorAt);
37826
- const computedAtMs = typeof computedAt === "string" && computedAt.trim() ? parseTimestampMs(computedAt) : Number.NaN;
37827
37853
  const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs2);
37828
- const tickOriginAtMs = Number.isFinite(computedAtMs) ? computedAtMs : anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
37854
+ const tickOriginAtMs = anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
37829
37855
  if (workspace.video_grid_green_streak_active === true && isFiniteNumber3(streakMinutes) && streakMinutes > 0 && startedAt && anchorAt && Number.isFinite(startedAtMs) && isFresh) {
37830
37856
  return {
37831
37857
  startedAt,
@@ -63964,6 +63990,8 @@ var HelpView = ({
63964
63990
  };
63965
63991
  var AuthenticatedHelpView = withAuth(HelpView);
63966
63992
  var HelpView_default = HelpView;
63993
+ var REALTIME_REFRESH_DEBOUNCE_MS2 = 1500;
63994
+ var REALTIME_REFRESH_MIN_INTERVAL_MS2 = 5e3;
63967
63995
  var transformActiveBreaks = (activeBreaksByLine) => {
63968
63996
  if (!activeBreaksByLine) return [];
63969
63997
  return Object.values(activeBreaksByLine).flat().map((item) => ({
@@ -64002,6 +64030,17 @@ var normalizeMetadata = (metadata) => ({
64002
64030
  cacheStatus: metadata?.cache_status,
64003
64031
  warnings: metadata?.warnings ?? []
64004
64032
  });
64033
+ var createResolvedScopeLookup = (resolvedScope, workspaces, blueComparisonWorkspaces) => {
64034
+ const lookup = /* @__PURE__ */ new Set();
64035
+ const addEntry = (lineId, date, shiftId) => {
64036
+ if (!lineId || !date || shiftId === void 0 || shiftId === null) return;
64037
+ lookup.add(`${lineId}|${date}|${shiftId}`);
64038
+ };
64039
+ resolvedScope.forEach((entry) => addEntry(entry.line_id, entry.date, entry.shift_id));
64040
+ workspaces.forEach((workspace) => addEntry(workspace.line_id, workspace.date, workspace.shift_id));
64041
+ blueComparisonWorkspaces.forEach((workspace) => addEntry(workspace.line_id, workspace.date, workspace.shift_id));
64042
+ return lookup;
64043
+ };
64005
64044
  var useLiveMonitorBootstrap = ({
64006
64045
  lineIds,
64007
64046
  blueComparisonLineIds,
@@ -64028,20 +64067,34 @@ var useLiveMonitorBootstrap = ({
64028
64067
  () => Array.from(new Set(rawBlueComparisonLineIdsKey ? rawBlueComparisonLineIdsKey.split(",") : [])),
64029
64068
  [rawBlueComparisonLineIdsKey]
64030
64069
  );
64070
+ const realtimeLineIds = React144.useMemo(
64071
+ () => Array.from(new Set([...normalizedLineIds, ...normalizedBlueComparisonLineIds].filter(Boolean))).sort(),
64072
+ [normalizedLineIds, normalizedBlueComparisonLineIds]
64073
+ );
64031
64074
  const requestKey = React144.useMemo(
64032
64075
  () => `${normalizedLineIds.slice().sort().join(",")}|blue:${normalizedBlueComparisonLineIds.slice().sort().join(",")}`,
64033
64076
  [normalizedLineIds, normalizedBlueComparisonLineIds]
64034
64077
  );
64078
+ const realtimeLineIdsKey = React144.useMemo(() => realtimeLineIds.join(","), [realtimeLineIds]);
64079
+ const companySpecificMetricsTable = React144.useMemo(
64080
+ () => getCompanyMetricsTableName(resolvedCompanyId, "performance_metrics"),
64081
+ [resolvedCompanyId]
64082
+ );
64035
64083
  const [state, setState] = React144.useState(() => createEmptyState());
64036
64084
  const [rawState, setRawState] = React144.useState(null);
64037
64085
  const [isLoading, setIsLoading] = React144.useState(false);
64038
64086
  const [error, setError] = React144.useState(null);
64039
64087
  const activeRequestIdRef = React144.useRef(0);
64088
+ const isFetchingRef = React144.useRef(false);
64089
+ const pendingRealtimeRefreshRef = React144.useRef(false);
64090
+ const realtimeRefreshTimerRef = React144.useRef(null);
64091
+ const lastRealtimeRefreshStartedAtRef = React144.useRef(0);
64040
64092
  const fetchBootstrap = React144.useCallback(async (force = false) => {
64041
64093
  if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
64042
64094
  return;
64043
64095
  }
64044
64096
  const requestId = ++activeRequestIdRef.current;
64097
+ isFetchingRef.current = true;
64045
64098
  setIsLoading(true);
64046
64099
  setError(null);
64047
64100
  try {
@@ -64054,6 +64107,7 @@ var useLiveMonitorBootstrap = ({
64054
64107
  if (force) {
64055
64108
  searchParams.set("force_refresh", "true");
64056
64109
  }
64110
+ appendQaGreenStreakSearchParams(searchParams);
64057
64111
  const response = await fetchBackendJson(
64058
64112
  supabase,
64059
64113
  `/api/dashboard/monitor-bootstrap?${searchParams.toString()}`,
@@ -64091,6 +64145,7 @@ var useLiveMonitorBootstrap = ({
64091
64145
  } finally {
64092
64146
  if (requestId === activeRequestIdRef.current) {
64093
64147
  setIsLoading(false);
64148
+ isFetchingRef.current = false;
64094
64149
  }
64095
64150
  }
64096
64151
  }, [
@@ -64101,6 +64156,52 @@ var useLiveMonitorBootstrap = ({
64101
64156
  normalizedBlueComparisonLineIds,
64102
64157
  requestKey
64103
64158
  ]);
64159
+ const fetchBootstrapRef = React144.useRef(fetchBootstrap);
64160
+ React144.useEffect(() => {
64161
+ fetchBootstrapRef.current = fetchBootstrap;
64162
+ }, [fetchBootstrap]);
64163
+ const clearRealtimeRefreshTimer = React144.useCallback(() => {
64164
+ if (realtimeRefreshTimerRef.current !== null) {
64165
+ window.clearTimeout(realtimeRefreshTimerRef.current);
64166
+ realtimeRefreshTimerRef.current = null;
64167
+ }
64168
+ }, []);
64169
+ const scheduleRealtimeRefresh = React144.useCallback(() => {
64170
+ if (!enabled || !supabase || realtimeRefreshTimerRef.current !== null) {
64171
+ return;
64172
+ }
64173
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
64174
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS2 - elapsedSinceLastRealtimeRefresh);
64175
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS2, minIntervalRemaining);
64176
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
64177
+ realtimeRefreshTimerRef.current = null;
64178
+ if (!pendingRealtimeRefreshRef.current) {
64179
+ return;
64180
+ }
64181
+ if (isFetchingRef.current) {
64182
+ scheduleRealtimeRefresh();
64183
+ return;
64184
+ }
64185
+ pendingRealtimeRefreshRef.current = false;
64186
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
64187
+ void fetchBootstrapRef.current(true);
64188
+ }, nextDelay);
64189
+ }, [enabled, supabase]);
64190
+ const queueRealtimeRefresh = React144.useCallback(() => {
64191
+ if (!enabled || !supabase) {
64192
+ pendingRealtimeRefreshRef.current = false;
64193
+ clearRealtimeRefreshTimer();
64194
+ return;
64195
+ }
64196
+ pendingRealtimeRefreshRef.current = true;
64197
+ scheduleRealtimeRefresh();
64198
+ }, [clearRealtimeRefreshTimer, enabled, scheduleRealtimeRefresh, supabase]);
64199
+ React144.useEffect(() => {
64200
+ return () => {
64201
+ pendingRealtimeRefreshRef.current = false;
64202
+ clearRealtimeRefreshTimer();
64203
+ };
64204
+ }, [clearRealtimeRefreshTimer]);
64104
64205
  React144.useEffect(() => {
64105
64206
  if (!enabled || !rawState || !resolvedCompanyId) {
64106
64207
  return;
@@ -64179,6 +64280,83 @@ var useLiveMonitorBootstrap = ({
64179
64280
  }
64180
64281
  void fetchBootstrap(false);
64181
64282
  }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
64283
+ const realtimeScopeKey = React144.useMemo(() => Array.from(createResolvedScopeLookup(
64284
+ state.resolvedScope,
64285
+ state.workspaceMetrics,
64286
+ state.blueComparisonWorkspaceMetrics
64287
+ )).sort().join("||"), [state.resolvedScope, state.workspaceMetrics, state.blueComparisonWorkspaceMetrics]);
64288
+ React144.useEffect(() => {
64289
+ if (!enabled || !resolvedCompanyId || !realtimeLineIdsKey || !supabase) {
64290
+ return void 0;
64291
+ }
64292
+ const scopeLookup = new Set(realtimeScopeKey ? realtimeScopeKey.split("||") : []);
64293
+ if (scopeLookup.size === 0) {
64294
+ return void 0;
64295
+ }
64296
+ const realtimeLineIdsForFilter = realtimeLineIdsKey.split(",").filter(Boolean);
64297
+ if (realtimeLineIdsForFilter.length === 0) {
64298
+ return void 0;
64299
+ }
64300
+ const lineIdFilter = `line_id=in.(${realtimeLineIdsForFilter.join(",")})`;
64301
+ const channels = [];
64302
+ const shouldRefreshForPayload = (payload) => {
64303
+ const payloadData = payload.new || payload.old;
64304
+ return scopeLookup.has(`${payloadData?.line_id}|${payloadData?.date}|${payloadData?.shift_id}`);
64305
+ };
64306
+ const createSubscription = (table, channelNameBase) => {
64307
+ const channelName = `${channelNameBase}-${state.scopeKey || requestKey}`.replace(/[^a-zA-Z0-9_-]/g, "");
64308
+ const channel = supabase.channel(channelName).on(
64309
+ "postgres_changes",
64310
+ { event: "*", schema: "public", table, filter: lineIdFilter },
64311
+ (payload) => {
64312
+ if (shouldRefreshForPayload(payload)) {
64313
+ queueRealtimeRefresh();
64314
+ }
64315
+ }
64316
+ ).subscribe();
64317
+ channels.push(channel);
64318
+ };
64319
+ createSubscription(companySpecificMetricsTable, "monitor-bootstrap-ws");
64320
+ createSubscription("line_metrics", "monitor-bootstrap-lm");
64321
+ createSubscription("wip_buffer_metrics", "monitor-bootstrap-wip-metrics");
64322
+ createSubscription("wip_buffer_alerts", "monitor-bootstrap-wip-alerts");
64323
+ return () => {
64324
+ channels.forEach((channel) => {
64325
+ supabase.removeChannel(channel);
64326
+ });
64327
+ };
64328
+ }, [
64329
+ enabled,
64330
+ resolvedCompanyId,
64331
+ realtimeLineIdsKey,
64332
+ supabase,
64333
+ realtimeScopeKey,
64334
+ state.scopeKey,
64335
+ requestKey,
64336
+ companySpecificMetricsTable,
64337
+ queueRealtimeRefresh
64338
+ ]);
64339
+ React144.useEffect(() => {
64340
+ if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
64341
+ return void 0;
64342
+ }
64343
+ const forceRefreshOnResume = () => {
64344
+ void fetchBootstrapRef.current(true);
64345
+ };
64346
+ const handleVisibilityChange = () => {
64347
+ if (document.visibilityState === "visible") {
64348
+ forceRefreshOnResume();
64349
+ }
64350
+ };
64351
+ document.addEventListener("visibilitychange", handleVisibilityChange);
64352
+ window.addEventListener("focus", forceRefreshOnResume);
64353
+ window.addEventListener("online", forceRefreshOnResume);
64354
+ return () => {
64355
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
64356
+ window.removeEventListener("focus", forceRefreshOnResume);
64357
+ window.removeEventListener("online", forceRefreshOnResume);
64358
+ };
64359
+ }, [enabled, normalizedLineIds.length, resolvedCompanyId, supabase]);
64182
64360
  React144.useEffect(() => {
64183
64361
  if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
64184
64362
  return void 0;
@@ -64933,7 +65111,6 @@ function HomeView({
64933
65111
  const allGreenMilestoneTrackingRef = React144.useRef({ identity: null, lastMilestoneSeconds: null });
64934
65112
  const allGreenStreakTimerBaselineRef = React144.useRef(null);
64935
65113
  const [showAllGreenCelebration, setShowAllGreenCelebration] = React144.useState(false);
64936
- const [allGreenCelebrationStartedAtMs, setAllGreenCelebrationStartedAtMs] = React144.useState(null);
64937
65114
  const [greenStreakMilestoneBanner, setGreenStreakMilestoneBanner] = React144.useState(null);
64938
65115
  const [allGreenStreakNowMs, setAllGreenStreakNowMs] = React144.useState(() => Date.now());
64939
65116
  const currentAllGreenStreakNowMs = Math.max(allGreenStreakNowMs, Date.now());
@@ -64996,13 +65173,11 @@ function HomeView({
64996
65173
  };
64997
65174
  }, [allGreenMilestoneIdentity, allGreenStreakDisplay, currentAllGreenStreakNowMs]);
64998
65175
  const allGreenCelebrationTimerText = React144.useMemo(() => {
64999
- if (!showAllGreenCelebration || allGreenCelebrationStartedAtMs === null) {
65176
+ if (!showAllGreenCelebration || !visibleAllGreenStreakDisplay) {
65000
65177
  return null;
65001
65178
  }
65002
- return formatAllGreenCelebrationTimer(
65003
- Math.max(0, Math.floor((currentAllGreenStreakNowMs - allGreenCelebrationStartedAtMs) / 1e3))
65004
- );
65005
- }, [allGreenCelebrationStartedAtMs, currentAllGreenStreakNowMs, showAllGreenCelebration]);
65179
+ return formatAllGreenCelebrationTimer(visibleAllGreenStreakDisplay.elapsedSeconds);
65180
+ }, [showAllGreenCelebration, visibleAllGreenStreakDisplay]);
65006
65181
  React144.useEffect(() => {
65007
65182
  const hasPossibleStreakTimer = showAllGreenCelebration || Boolean(allGreenStreakDisplay) || workspaceMetricsWithBreakState.some((workspace) => workspace.video_grid_green_streak_active === true);
65008
65183
  if (!hasPossibleStreakTimer) {
@@ -65021,17 +65196,14 @@ function HomeView({
65021
65196
  allGreenCelebrationTimerRef.current = null;
65022
65197
  }
65023
65198
  setShowAllGreenCelebration(false);
65024
- setAllGreenCelebrationStartedAtMs(null);
65025
65199
  }, []);
65026
65200
  const triggerAllGreenCelebration = React144.useCallback(() => {
65027
- setAllGreenCelebrationStartedAtMs(Date.now());
65028
65201
  setShowAllGreenCelebration(true);
65029
65202
  if (allGreenCelebrationTimerRef.current) {
65030
65203
  clearTimeout(allGreenCelebrationTimerRef.current);
65031
65204
  }
65032
65205
  allGreenCelebrationTimerRef.current = setTimeout(() => {
65033
65206
  setShowAllGreenCelebration(false);
65034
- setAllGreenCelebrationStartedAtMs(null);
65035
65207
  allGreenCelebrationTimerRef.current = null;
65036
65208
  }, ALL_GREEN_CELEBRATION_DURATION_MS);
65037
65209
  }, []);
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:", {
@@ -37791,12 +37819,10 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
37791
37819
  const startedAt = workspace.video_grid_green_streak_started_at;
37792
37820
  const anchorAt = workspace.video_grid_green_streak_anchor_at;
37793
37821
  const streakMinutes = workspace.video_grid_green_streak_minutes;
37794
- const computedAt = workspace.recent_flow_computed_at;
37795
37822
  const startedAtMs = parseTimestampMs(startedAt);
37796
37823
  const anchorAtMs = parseTimestampMs(anchorAt);
37797
- const computedAtMs = typeof computedAt === "string" && computedAt.trim() ? parseTimestampMs(computedAt) : Number.NaN;
37798
37824
  const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs2);
37799
- const tickOriginAtMs = Number.isFinite(computedAtMs) ? computedAtMs : anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
37825
+ const tickOriginAtMs = anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
37800
37826
  if (workspace.video_grid_green_streak_active === true && isFiniteNumber3(streakMinutes) && streakMinutes > 0 && startedAt && anchorAt && Number.isFinite(startedAtMs) && isFresh) {
37801
37827
  return {
37802
37828
  startedAt,
@@ -63935,6 +63961,8 @@ var HelpView = ({
63935
63961
  };
63936
63962
  var AuthenticatedHelpView = withAuth(HelpView);
63937
63963
  var HelpView_default = HelpView;
63964
+ var REALTIME_REFRESH_DEBOUNCE_MS2 = 1500;
63965
+ var REALTIME_REFRESH_MIN_INTERVAL_MS2 = 5e3;
63938
63966
  var transformActiveBreaks = (activeBreaksByLine) => {
63939
63967
  if (!activeBreaksByLine) return [];
63940
63968
  return Object.values(activeBreaksByLine).flat().map((item) => ({
@@ -63973,6 +64001,17 @@ var normalizeMetadata = (metadata) => ({
63973
64001
  cacheStatus: metadata?.cache_status,
63974
64002
  warnings: metadata?.warnings ?? []
63975
64003
  });
64004
+ var createResolvedScopeLookup = (resolvedScope, workspaces, blueComparisonWorkspaces) => {
64005
+ const lookup = /* @__PURE__ */ new Set();
64006
+ const addEntry = (lineId, date, shiftId) => {
64007
+ if (!lineId || !date || shiftId === void 0 || shiftId === null) return;
64008
+ lookup.add(`${lineId}|${date}|${shiftId}`);
64009
+ };
64010
+ resolvedScope.forEach((entry) => addEntry(entry.line_id, entry.date, entry.shift_id));
64011
+ workspaces.forEach((workspace) => addEntry(workspace.line_id, workspace.date, workspace.shift_id));
64012
+ blueComparisonWorkspaces.forEach((workspace) => addEntry(workspace.line_id, workspace.date, workspace.shift_id));
64013
+ return lookup;
64014
+ };
63976
64015
  var useLiveMonitorBootstrap = ({
63977
64016
  lineIds,
63978
64017
  blueComparisonLineIds,
@@ -63999,20 +64038,34 @@ var useLiveMonitorBootstrap = ({
63999
64038
  () => Array.from(new Set(rawBlueComparisonLineIdsKey ? rawBlueComparisonLineIdsKey.split(",") : [])),
64000
64039
  [rawBlueComparisonLineIdsKey]
64001
64040
  );
64041
+ const realtimeLineIds = useMemo(
64042
+ () => Array.from(new Set([...normalizedLineIds, ...normalizedBlueComparisonLineIds].filter(Boolean))).sort(),
64043
+ [normalizedLineIds, normalizedBlueComparisonLineIds]
64044
+ );
64002
64045
  const requestKey = useMemo(
64003
64046
  () => `${normalizedLineIds.slice().sort().join(",")}|blue:${normalizedBlueComparisonLineIds.slice().sort().join(",")}`,
64004
64047
  [normalizedLineIds, normalizedBlueComparisonLineIds]
64005
64048
  );
64049
+ const realtimeLineIdsKey = useMemo(() => realtimeLineIds.join(","), [realtimeLineIds]);
64050
+ const companySpecificMetricsTable = useMemo(
64051
+ () => getCompanyMetricsTableName(resolvedCompanyId, "performance_metrics"),
64052
+ [resolvedCompanyId]
64053
+ );
64006
64054
  const [state, setState] = useState(() => createEmptyState());
64007
64055
  const [rawState, setRawState] = useState(null);
64008
64056
  const [isLoading, setIsLoading] = useState(false);
64009
64057
  const [error, setError] = useState(null);
64010
64058
  const activeRequestIdRef = useRef(0);
64059
+ const isFetchingRef = useRef(false);
64060
+ const pendingRealtimeRefreshRef = useRef(false);
64061
+ const realtimeRefreshTimerRef = useRef(null);
64062
+ const lastRealtimeRefreshStartedAtRef = useRef(0);
64011
64063
  const fetchBootstrap = useCallback(async (force = false) => {
64012
64064
  if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
64013
64065
  return;
64014
64066
  }
64015
64067
  const requestId = ++activeRequestIdRef.current;
64068
+ isFetchingRef.current = true;
64016
64069
  setIsLoading(true);
64017
64070
  setError(null);
64018
64071
  try {
@@ -64025,6 +64078,7 @@ var useLiveMonitorBootstrap = ({
64025
64078
  if (force) {
64026
64079
  searchParams.set("force_refresh", "true");
64027
64080
  }
64081
+ appendQaGreenStreakSearchParams(searchParams);
64028
64082
  const response = await fetchBackendJson(
64029
64083
  supabase,
64030
64084
  `/api/dashboard/monitor-bootstrap?${searchParams.toString()}`,
@@ -64062,6 +64116,7 @@ var useLiveMonitorBootstrap = ({
64062
64116
  } finally {
64063
64117
  if (requestId === activeRequestIdRef.current) {
64064
64118
  setIsLoading(false);
64119
+ isFetchingRef.current = false;
64065
64120
  }
64066
64121
  }
64067
64122
  }, [
@@ -64072,6 +64127,52 @@ var useLiveMonitorBootstrap = ({
64072
64127
  normalizedBlueComparisonLineIds,
64073
64128
  requestKey
64074
64129
  ]);
64130
+ const fetchBootstrapRef = useRef(fetchBootstrap);
64131
+ useEffect(() => {
64132
+ fetchBootstrapRef.current = fetchBootstrap;
64133
+ }, [fetchBootstrap]);
64134
+ const clearRealtimeRefreshTimer = useCallback(() => {
64135
+ if (realtimeRefreshTimerRef.current !== null) {
64136
+ window.clearTimeout(realtimeRefreshTimerRef.current);
64137
+ realtimeRefreshTimerRef.current = null;
64138
+ }
64139
+ }, []);
64140
+ const scheduleRealtimeRefresh = useCallback(() => {
64141
+ if (!enabled || !supabase || realtimeRefreshTimerRef.current !== null) {
64142
+ return;
64143
+ }
64144
+ const elapsedSinceLastRealtimeRefresh = Date.now() - lastRealtimeRefreshStartedAtRef.current;
64145
+ const minIntervalRemaining = Math.max(0, REALTIME_REFRESH_MIN_INTERVAL_MS2 - elapsedSinceLastRealtimeRefresh);
64146
+ const nextDelay = Math.max(REALTIME_REFRESH_DEBOUNCE_MS2, minIntervalRemaining);
64147
+ realtimeRefreshTimerRef.current = window.setTimeout(() => {
64148
+ realtimeRefreshTimerRef.current = null;
64149
+ if (!pendingRealtimeRefreshRef.current) {
64150
+ return;
64151
+ }
64152
+ if (isFetchingRef.current) {
64153
+ scheduleRealtimeRefresh();
64154
+ return;
64155
+ }
64156
+ pendingRealtimeRefreshRef.current = false;
64157
+ lastRealtimeRefreshStartedAtRef.current = Date.now();
64158
+ void fetchBootstrapRef.current(true);
64159
+ }, nextDelay);
64160
+ }, [enabled, supabase]);
64161
+ const queueRealtimeRefresh = useCallback(() => {
64162
+ if (!enabled || !supabase) {
64163
+ pendingRealtimeRefreshRef.current = false;
64164
+ clearRealtimeRefreshTimer();
64165
+ return;
64166
+ }
64167
+ pendingRealtimeRefreshRef.current = true;
64168
+ scheduleRealtimeRefresh();
64169
+ }, [clearRealtimeRefreshTimer, enabled, scheduleRealtimeRefresh, supabase]);
64170
+ useEffect(() => {
64171
+ return () => {
64172
+ pendingRealtimeRefreshRef.current = false;
64173
+ clearRealtimeRefreshTimer();
64174
+ };
64175
+ }, [clearRealtimeRefreshTimer]);
64075
64176
  useEffect(() => {
64076
64177
  if (!enabled || !rawState || !resolvedCompanyId) {
64077
64178
  return;
@@ -64150,6 +64251,83 @@ var useLiveMonitorBootstrap = ({
64150
64251
  }
64151
64252
  void fetchBootstrap(false);
64152
64253
  }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
64254
+ const realtimeScopeKey = useMemo(() => Array.from(createResolvedScopeLookup(
64255
+ state.resolvedScope,
64256
+ state.workspaceMetrics,
64257
+ state.blueComparisonWorkspaceMetrics
64258
+ )).sort().join("||"), [state.resolvedScope, state.workspaceMetrics, state.blueComparisonWorkspaceMetrics]);
64259
+ useEffect(() => {
64260
+ if (!enabled || !resolvedCompanyId || !realtimeLineIdsKey || !supabase) {
64261
+ return void 0;
64262
+ }
64263
+ const scopeLookup = new Set(realtimeScopeKey ? realtimeScopeKey.split("||") : []);
64264
+ if (scopeLookup.size === 0) {
64265
+ return void 0;
64266
+ }
64267
+ const realtimeLineIdsForFilter = realtimeLineIdsKey.split(",").filter(Boolean);
64268
+ if (realtimeLineIdsForFilter.length === 0) {
64269
+ return void 0;
64270
+ }
64271
+ const lineIdFilter = `line_id=in.(${realtimeLineIdsForFilter.join(",")})`;
64272
+ const channels = [];
64273
+ const shouldRefreshForPayload = (payload) => {
64274
+ const payloadData = payload.new || payload.old;
64275
+ return scopeLookup.has(`${payloadData?.line_id}|${payloadData?.date}|${payloadData?.shift_id}`);
64276
+ };
64277
+ const createSubscription = (table, channelNameBase) => {
64278
+ const channelName = `${channelNameBase}-${state.scopeKey || requestKey}`.replace(/[^a-zA-Z0-9_-]/g, "");
64279
+ const channel = supabase.channel(channelName).on(
64280
+ "postgres_changes",
64281
+ { event: "*", schema: "public", table, filter: lineIdFilter },
64282
+ (payload) => {
64283
+ if (shouldRefreshForPayload(payload)) {
64284
+ queueRealtimeRefresh();
64285
+ }
64286
+ }
64287
+ ).subscribe();
64288
+ channels.push(channel);
64289
+ };
64290
+ createSubscription(companySpecificMetricsTable, "monitor-bootstrap-ws");
64291
+ createSubscription("line_metrics", "monitor-bootstrap-lm");
64292
+ createSubscription("wip_buffer_metrics", "monitor-bootstrap-wip-metrics");
64293
+ createSubscription("wip_buffer_alerts", "monitor-bootstrap-wip-alerts");
64294
+ return () => {
64295
+ channels.forEach((channel) => {
64296
+ supabase.removeChannel(channel);
64297
+ });
64298
+ };
64299
+ }, [
64300
+ enabled,
64301
+ resolvedCompanyId,
64302
+ realtimeLineIdsKey,
64303
+ supabase,
64304
+ realtimeScopeKey,
64305
+ state.scopeKey,
64306
+ requestKey,
64307
+ companySpecificMetricsTable,
64308
+ queueRealtimeRefresh
64309
+ ]);
64310
+ useEffect(() => {
64311
+ if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
64312
+ return void 0;
64313
+ }
64314
+ const forceRefreshOnResume = () => {
64315
+ void fetchBootstrapRef.current(true);
64316
+ };
64317
+ const handleVisibilityChange = () => {
64318
+ if (document.visibilityState === "visible") {
64319
+ forceRefreshOnResume();
64320
+ }
64321
+ };
64322
+ document.addEventListener("visibilitychange", handleVisibilityChange);
64323
+ window.addEventListener("focus", forceRefreshOnResume);
64324
+ window.addEventListener("online", forceRefreshOnResume);
64325
+ return () => {
64326
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
64327
+ window.removeEventListener("focus", forceRefreshOnResume);
64328
+ window.removeEventListener("online", forceRefreshOnResume);
64329
+ };
64330
+ }, [enabled, normalizedLineIds.length, resolvedCompanyId, supabase]);
64153
64331
  useEffect(() => {
64154
64332
  if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
64155
64333
  return void 0;
@@ -64904,7 +65082,6 @@ function HomeView({
64904
65082
  const allGreenMilestoneTrackingRef = useRef({ identity: null, lastMilestoneSeconds: null });
64905
65083
  const allGreenStreakTimerBaselineRef = useRef(null);
64906
65084
  const [showAllGreenCelebration, setShowAllGreenCelebration] = useState(false);
64907
- const [allGreenCelebrationStartedAtMs, setAllGreenCelebrationStartedAtMs] = useState(null);
64908
65085
  const [greenStreakMilestoneBanner, setGreenStreakMilestoneBanner] = useState(null);
64909
65086
  const [allGreenStreakNowMs, setAllGreenStreakNowMs] = useState(() => Date.now());
64910
65087
  const currentAllGreenStreakNowMs = Math.max(allGreenStreakNowMs, Date.now());
@@ -64967,13 +65144,11 @@ function HomeView({
64967
65144
  };
64968
65145
  }, [allGreenMilestoneIdentity, allGreenStreakDisplay, currentAllGreenStreakNowMs]);
64969
65146
  const allGreenCelebrationTimerText = useMemo(() => {
64970
- if (!showAllGreenCelebration || allGreenCelebrationStartedAtMs === null) {
65147
+ if (!showAllGreenCelebration || !visibleAllGreenStreakDisplay) {
64971
65148
  return null;
64972
65149
  }
64973
- return formatAllGreenCelebrationTimer(
64974
- Math.max(0, Math.floor((currentAllGreenStreakNowMs - allGreenCelebrationStartedAtMs) / 1e3))
64975
- );
64976
- }, [allGreenCelebrationStartedAtMs, currentAllGreenStreakNowMs, showAllGreenCelebration]);
65150
+ return formatAllGreenCelebrationTimer(visibleAllGreenStreakDisplay.elapsedSeconds);
65151
+ }, [showAllGreenCelebration, visibleAllGreenStreakDisplay]);
64977
65152
  useEffect(() => {
64978
65153
  const hasPossibleStreakTimer = showAllGreenCelebration || Boolean(allGreenStreakDisplay) || workspaceMetricsWithBreakState.some((workspace) => workspace.video_grid_green_streak_active === true);
64979
65154
  if (!hasPossibleStreakTimer) {
@@ -64992,17 +65167,14 @@ function HomeView({
64992
65167
  allGreenCelebrationTimerRef.current = null;
64993
65168
  }
64994
65169
  setShowAllGreenCelebration(false);
64995
- setAllGreenCelebrationStartedAtMs(null);
64996
65170
  }, []);
64997
65171
  const triggerAllGreenCelebration = useCallback(() => {
64998
- setAllGreenCelebrationStartedAtMs(Date.now());
64999
65172
  setShowAllGreenCelebration(true);
65000
65173
  if (allGreenCelebrationTimerRef.current) {
65001
65174
  clearTimeout(allGreenCelebrationTimerRef.current);
65002
65175
  }
65003
65176
  allGreenCelebrationTimerRef.current = setTimeout(() => {
65004
65177
  setShowAllGreenCelebration(false);
65005
- setAllGreenCelebrationStartedAtMs(null);
65006
65178
  allGreenCelebrationTimerRef.current = null;
65007
65179
  }, ALL_GREEN_CELEBRATION_DURATION_MS);
65008
65180
  }, []);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.12.23",
3
+ "version": "6.12.25",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",