@optifye/dashboard-core 6.12.38 → 6.12.39

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 +172 -48
  2. package/dist/index.mjs +172 -48
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -37286,7 +37286,9 @@ var HourlyOutputChartComponent = ({
37286
37286
  const idleSlot = idleSlots[i];
37287
37287
  const explicitTarget = hasHourlyTargetOutputProp ? effectiveHourlyTargetOutput?.[i] ?? null : void 0;
37288
37288
  const currentTarget = hasHourlyTargetOutputProp ? explicitTarget : targets[i] || pphThreshold;
37289
+ const outputForComparison = Math.round(animatedData[i] || 0);
37289
37290
  const comparisonTarget = currentTarget === null || currentTarget === void 0 ? targets[i] || pphThreshold : currentTarget;
37291
+ const targetForComparison = Math.round(comparisonTarget);
37290
37292
  return {
37291
37293
  hourIndex: idleSlot?.hourIndex ?? i,
37292
37294
  hour: idleSlot?.hour || "",
@@ -37295,7 +37297,7 @@ var HourlyOutputChartComponent = ({
37295
37297
  originalOutput: data[i] || 0,
37296
37298
  // Keep original data for labels
37297
37299
  target: currentTarget ?? null,
37298
- color: (animatedData[i] || 0) >= comparisonTarget ? "#00AB45" : "#E34329",
37300
+ color: outputForComparison >= targetForComparison ? "#00AB45" : "#E34329",
37299
37301
  idleMinutes: idleSlot?.idleMinutes || 0,
37300
37302
  idleArray: idleSlot?.idleArray || [],
37301
37303
  skuIndex: i,
@@ -59665,6 +59667,42 @@ var buildSnapshotRows = (entries, lines) => {
59665
59667
  rowType: row.rowType
59666
59668
  }));
59667
59669
  };
59670
+ var isValidSnapshotRow = (row) => !!row && typeof row.efficiency === "number" && Number.isFinite(row.efficiency) && typeof row.rank === "number" && Number.isFinite(row.rank);
59671
+ var refreshPendingEventsFromSnapshot = (events, currentSnapshot) => {
59672
+ if (events.length === 0 || currentSnapshot.length === 0) return [];
59673
+ const snapshotByRowId = new Map(currentSnapshot.map((row) => [row.lineId, row]));
59674
+ return events.flatMap((event) => {
59675
+ const currentSubject = snapshotByRowId.get(event.crossingLineId);
59676
+ if (!isValidSnapshotRow(currentSubject)) return [];
59677
+ const movedFromOriginalRank = event.direction === "up" ? currentSubject.rank < event.previousRank : currentSubject.rank > event.previousRank;
59678
+ if (!movedFromOriginalRank) return [];
59679
+ const crossedLines = event.crossedLines.flatMap((crossedLine) => {
59680
+ const currentCrossedLine = snapshotByRowId.get(crossedLine.lineId);
59681
+ if (!isValidSnapshotRow(currentCrossedLine)) return [];
59682
+ const stillCrossed = event.direction === "up" ? currentSubject.rank < currentCrossedLine.rank : currentSubject.rank > currentCrossedLine.rank;
59683
+ if (!stillCrossed) return [];
59684
+ return [{
59685
+ ...crossedLine,
59686
+ lineName: currentCrossedLine.lineName,
59687
+ currentRank: currentCrossedLine.rank,
59688
+ currentEfficiency: currentCrossedLine.efficiency
59689
+ }];
59690
+ });
59691
+ if (crossedLines.length !== event.crossedLines.length) return [];
59692
+ return [{
59693
+ ...event,
59694
+ crossingLineName: currentSubject.lineName,
59695
+ currentRank: currentSubject.rank,
59696
+ currentEfficiency: currentSubject.efficiency,
59697
+ crossedLines,
59698
+ eventKey: buildLineOvertakeEventKey(
59699
+ event.crossingLineId,
59700
+ event.direction,
59701
+ crossedLines.map((line) => line.lineId)
59702
+ )
59703
+ }];
59704
+ });
59705
+ };
59668
59706
  var formatCrossedLineNames = (event) => {
59669
59707
  const names = event.crossedLines.map((line) => line.lineName).filter(Boolean);
59670
59708
  if (names.length === 0) return "another line";
@@ -59722,6 +59760,7 @@ var storeEventKey = (scopeKey, eventKey) => {
59722
59760
  } catch {
59723
59761
  }
59724
59762
  };
59763
+ var buildAcknowledgementKey = (scopeKey, eventKey) => `${scopeKey}:${eventKey}`;
59725
59764
  var LineOvertakeNotificationPopup = ({
59726
59765
  events,
59727
59766
  onDismiss,
@@ -59871,6 +59910,7 @@ var LineOvertakeNotificationManager = ({
59871
59910
  const [visibleLines, setVisibleLines] = React146.useState([]);
59872
59911
  const [linesLoaded, setLinesLoaded] = React146.useState(false);
59873
59912
  const [notificationEvents, setNotificationEvents] = React146.useState([]);
59913
+ const [returnValidationPending, setReturnValidationPending] = React146.useState(false);
59874
59914
  const isOnActiveToastSurface = isActiveToastSurface(router$1.asPath);
59875
59915
  const qaSimulationMode = isOnActiveToastSurface ? getLocalQaSimulationMode(router$1.asPath) : null;
59876
59916
  const qaSimulationEnabled = Boolean(qaSimulationMode);
@@ -59897,20 +59937,33 @@ var LineOvertakeNotificationManager = ({
59897
59937
  const scopeKeyRef = React146.useRef("");
59898
59938
  const pollInFlightRef = React146.useRef(false);
59899
59939
  const notificationEventsRef = React146.useRef([]);
59940
+ const acknowledgedEventKeysRef = React146.useRef(/* @__PURE__ */ new Set());
59941
+ const wasOnActiveToastSurfaceRef = React146.useRef(isOnActiveToastSurface);
59900
59942
  const dedupeScopeKey = React146.useMemo(
59901
- () => [
59902
- user?.id || "anonymous",
59903
- companyId || "no-company",
59904
- currentShift.date,
59905
- currentShift.shiftId,
59906
- roleMode || "disabled",
59907
- watchedLineIds.slice().sort().join(",")
59908
- ].join("|"),
59943
+ () => {
59944
+ if (qaSimulationEnabled) {
59945
+ return [
59946
+ user?.id || "anonymous",
59947
+ "qa-simulation",
59948
+ qaSimulationMode || "default"
59949
+ ].join("|");
59950
+ }
59951
+ return [
59952
+ user?.id || "anonymous",
59953
+ companyId || "no-company",
59954
+ currentShift.date,
59955
+ currentShift.shiftId,
59956
+ roleMode || "disabled",
59957
+ watchedLineIds.slice().sort().join(",")
59958
+ ].join("|");
59959
+ },
59909
59960
  [
59910
59961
  user?.id,
59911
59962
  companyId,
59912
59963
  currentShift.date,
59913
59964
  currentShift.shiftId,
59965
+ qaSimulationEnabled,
59966
+ qaSimulationMode,
59914
59967
  roleMode,
59915
59968
  watchedLineIds
59916
59969
  ]
@@ -59947,13 +60000,45 @@ var LineOvertakeNotificationManager = ({
59947
60000
  previousSnapshotRef.current = null;
59948
60001
  notificationEventsRef.current = [];
59949
60002
  setNotificationEvents([]);
60003
+ setReturnValidationPending(false);
59950
60004
  scopeKeyRef.current = scopeKey;
59951
60005
  }, [isOnActiveToastSurface, scopeKey]);
59952
60006
  React146.useEffect(() => {
59953
- if (isOnActiveToastSurface) return;
60007
+ const wasOnActiveToastSurface = wasOnActiveToastSurfaceRef.current;
60008
+ wasOnActiveToastSurfaceRef.current = isOnActiveToastSurface;
60009
+ if (wasOnActiveToastSurface && !isOnActiveToastSurface && notificationEventsRef.current.length > 0) {
60010
+ setReturnValidationPending(true);
60011
+ return;
60012
+ }
60013
+ if (!wasOnActiveToastSurface && isOnActiveToastSurface && notificationEventsRef.current.length === 0) {
60014
+ setReturnValidationPending(false);
60015
+ }
60016
+ }, [isOnActiveToastSurface]);
60017
+ const acknowledgeCurrentEvents = React146.useCallback(() => {
60018
+ notificationEventsRef.current.forEach((event) => {
60019
+ acknowledgedEventKeysRef.current.add(buildAcknowledgementKey(dedupeScopeKey, event.eventKey));
60020
+ storeEventKey(dedupeScopeKey, event.eventKey);
60021
+ });
59954
60022
  notificationEventsRef.current = [];
59955
60023
  setNotificationEvents([]);
59956
- }, [isOnActiveToastSurface]);
60024
+ setReturnValidationPending(false);
60025
+ }, [dedupeScopeKey]);
60026
+ const clearPendingEvents = React146.useCallback(() => {
60027
+ notificationEventsRef.current = [];
60028
+ setNotificationEvents([]);
60029
+ setReturnValidationPending(false);
60030
+ }, []);
60031
+ const finishReturnValidation = React146.useCallback((events, currentSnapshot) => {
60032
+ if (events.length === 0 && notificationEventsRef.current.length > 0) {
60033
+ const validatedEvents = refreshPendingEventsFromSnapshot(
60034
+ notificationEventsRef.current,
60035
+ currentSnapshot
60036
+ );
60037
+ notificationEventsRef.current = validatedEvents;
60038
+ setNotificationEvents(validatedEvents);
60039
+ }
60040
+ setReturnValidationPending(false);
60041
+ }, []);
59957
60042
  React146.useEffect(() => {
59958
60043
  let cancelled = false;
59959
60044
  const loadLines = async () => {
@@ -59994,10 +60079,9 @@ var LineOvertakeNotificationManager = ({
59994
60079
  };
59995
60080
  }, [companyId, isOnActiveToastSurface, qaSimulationEnabled, roleMode, supabase]);
59996
60081
  const openLeaderboard = React146.useCallback(() => {
59997
- notificationEventsRef.current = [];
59998
- setNotificationEvents([]);
60082
+ acknowledgeCurrentEvents();
59999
60083
  void router$1.push("/kpis?tab=leaderboard");
60000
- }, [router$1]);
60084
+ }, [acknowledgeCurrentEvents, router$1]);
60001
60085
  const updateOvertakeEvents = React146.useCallback((events) => {
60002
60086
  if (events.length === 0) return;
60003
60087
  const currentEventByRowId = new Map(
@@ -60005,13 +60089,9 @@ var LineOvertakeNotificationManager = ({
60005
60089
  );
60006
60090
  const nextEvents = events.filter((event) => {
60007
60091
  if (currentEventByRowId.has(event.crossingLineId)) return true;
60008
- return !hasStoredEventKey(dedupeScopeKey, event.eventKey);
60092
+ return !(acknowledgedEventKeysRef.current.has(buildAcknowledgementKey(dedupeScopeKey, event.eventKey)) || hasStoredEventKey(dedupeScopeKey, event.eventKey));
60009
60093
  });
60010
60094
  if (nextEvents.length === 0) return;
60011
- nextEvents.forEach((event) => {
60012
- if (currentEventByRowId.has(event.crossingLineId)) return;
60013
- storeEventKey(dedupeScopeKey, event.eventKey);
60014
- });
60015
60095
  setNotificationEvents((currentEvents) => {
60016
60096
  const eventByRowId = new Map(currentEvents.map((event) => [event.crossingLineId, event]));
60017
60097
  nextEvents.forEach((event) => {
@@ -60054,6 +60134,7 @@ var LineOvertakeNotificationManager = ({
60054
60134
  });
60055
60135
  previousSnapshotRef.current = QA_CROSSED_ROWS;
60056
60136
  updateOvertakeEvents(events);
60137
+ finishReturnValidation(events, QA_CROSSED_ROWS);
60057
60138
  } finally {
60058
60139
  pollInFlightRef.current = false;
60059
60140
  }
@@ -60076,11 +60157,17 @@ var LineOvertakeNotificationManager = ({
60076
60157
  const currentSnapshot = buildSnapshotRows(entries, visibleLines);
60077
60158
  if (currentSnapshot.length === 0) {
60078
60159
  previousSnapshotRef.current = null;
60160
+ if (returnValidationPending) {
60161
+ clearPendingEvents();
60162
+ }
60079
60163
  return;
60080
60164
  }
60081
60165
  const previousSnapshot = previousSnapshotRef.current;
60082
60166
  previousSnapshotRef.current = currentSnapshot;
60083
- if (!previousSnapshot) return;
60167
+ if (!previousSnapshot) {
60168
+ finishReturnValidation([], currentSnapshot);
60169
+ return;
60170
+ }
60084
60171
  const events = detectLineOvertakeEvents({
60085
60172
  roleMode,
60086
60173
  watchedLineIds,
@@ -60088,6 +60175,7 @@ var LineOvertakeNotificationManager = ({
60088
60175
  currentRows: currentSnapshot
60089
60176
  });
60090
60177
  updateOvertakeEvents(events);
60178
+ finishReturnValidation(events, currentSnapshot);
60091
60179
  } catch (error) {
60092
60180
  console.warn("[LineOvertakeNotificationManager] Failed to poll daily leaderboard:", error);
60093
60181
  } finally {
@@ -60101,8 +60189,11 @@ var LineOvertakeNotificationManager = ({
60101
60189
  linesLoaded,
60102
60190
  qaSimulationEnabled,
60103
60191
  qaSimulationMode,
60192
+ returnValidationPending,
60104
60193
  roleMode,
60105
60194
  supabase,
60195
+ clearPendingEvents,
60196
+ finishReturnValidation,
60106
60197
  updateOvertakeEvents,
60107
60198
  visibleLineIds,
60108
60199
  watchedLineIds
@@ -60120,14 +60211,12 @@ var LineOvertakeNotificationManager = ({
60120
60211
  };
60121
60212
  }, [pollIntervalMs, pollLeaderboard]);
60122
60213
  if (!isOnActiveToastSurface) return null;
60214
+ if (returnValidationPending) return null;
60123
60215
  return /* @__PURE__ */ jsxRuntime.jsx(
60124
60216
  LineOvertakeNotificationPopup,
60125
60217
  {
60126
60218
  events: notificationEvents,
60127
- onDismiss: () => {
60128
- notificationEventsRef.current = [];
60129
- setNotificationEvents([]);
60130
- },
60219
+ onDismiss: acknowledgeCurrentEvents,
60131
60220
  onOpenLeaderboard: openLeaderboard
60132
60221
  }
60133
60222
  );
@@ -65305,6 +65394,7 @@ var useLiveMonitorBootstrap = ({
65305
65394
  const activeRequestIdRef = React146.useRef(0);
65306
65395
  const isFetchingRef = React146.useRef(false);
65307
65396
  const pendingRealtimeRefreshRef = React146.useRef(false);
65397
+ const pendingForcedRefreshRef = React146.useRef(false);
65308
65398
  const realtimeRefreshTimerRef = React146.useRef(null);
65309
65399
  const lastRealtimeRefreshStartedAtRef = React146.useRef(0);
65310
65400
  const fetchBootstrap = React146.useCallback(async (force = false) => {
@@ -65378,6 +65468,18 @@ var useLiveMonitorBootstrap = ({
65378
65468
  React146.useEffect(() => {
65379
65469
  fetchBootstrapRef.current = fetchBootstrap;
65380
65470
  }, [fetchBootstrap]);
65471
+ const requestScheduledForcedRefresh = React146.useCallback(() => {
65472
+ if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
65473
+ pendingForcedRefreshRef.current = false;
65474
+ return;
65475
+ }
65476
+ if (isFetchingRef.current) {
65477
+ pendingForcedRefreshRef.current = true;
65478
+ return;
65479
+ }
65480
+ pendingForcedRefreshRef.current = false;
65481
+ void fetchBootstrapRef.current(true);
65482
+ }, [enabled, normalizedLineIds.length, resolvedCompanyId, supabase]);
65381
65483
  const clearRealtimeRefreshTimer = React146.useCallback(() => {
65382
65484
  if (realtimeRefreshTimerRef.current !== null) {
65383
65485
  window.clearTimeout(realtimeRefreshTimerRef.current);
@@ -65402,9 +65504,9 @@ var useLiveMonitorBootstrap = ({
65402
65504
  }
65403
65505
  pendingRealtimeRefreshRef.current = false;
65404
65506
  lastRealtimeRefreshStartedAtRef.current = Date.now();
65405
- void fetchBootstrapRef.current(true);
65507
+ requestScheduledForcedRefresh();
65406
65508
  }, nextDelay);
65407
- }, [enabled, supabase]);
65509
+ }, [enabled, requestScheduledForcedRefresh, supabase]);
65408
65510
  const queueRealtimeRefresh = React146.useCallback(() => {
65409
65511
  if (!enabled || !supabase) {
65410
65512
  pendingRealtimeRefreshRef.current = false;
@@ -65417,9 +65519,15 @@ var useLiveMonitorBootstrap = ({
65417
65519
  React146.useEffect(() => {
65418
65520
  return () => {
65419
65521
  pendingRealtimeRefreshRef.current = false;
65522
+ pendingForcedRefreshRef.current = false;
65420
65523
  clearRealtimeRefreshTimer();
65421
65524
  };
65422
65525
  }, [clearRealtimeRefreshTimer]);
65526
+ React146.useEffect(() => {
65527
+ if (!isLoading && pendingForcedRefreshRef.current) {
65528
+ requestScheduledForcedRefresh();
65529
+ }
65530
+ }, [isLoading, requestScheduledForcedRefresh]);
65423
65531
  React146.useEffect(() => {
65424
65532
  if (!enabled || !rawState || !resolvedCompanyId) {
65425
65533
  return;
@@ -65558,9 +65666,7 @@ var useLiveMonitorBootstrap = ({
65558
65666
  if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
65559
65667
  return void 0;
65560
65668
  }
65561
- const forceRefreshOnResume = () => {
65562
- void fetchBootstrapRef.current(true);
65563
- };
65669
+ const forceRefreshOnResume = () => requestScheduledForcedRefresh();
65564
65670
  const handleVisibilityChange = () => {
65565
65671
  if (document.visibilityState === "visible") {
65566
65672
  forceRefreshOnResume();
@@ -65574,7 +65680,7 @@ var useLiveMonitorBootstrap = ({
65574
65680
  window.removeEventListener("focus", forceRefreshOnResume);
65575
65681
  window.removeEventListener("online", forceRefreshOnResume);
65576
65682
  };
65577
- }, [enabled, normalizedLineIds.length, resolvedCompanyId, supabase]);
65683
+ }, [enabled, normalizedLineIds.length, requestScheduledForcedRefresh, resolvedCompanyId, supabase]);
65578
65684
  React146.useEffect(() => {
65579
65685
  if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
65580
65686
  return void 0;
@@ -65582,7 +65688,7 @@ var useLiveMonitorBootstrap = ({
65582
65688
  let intervalId = null;
65583
65689
  let timeoutId = null;
65584
65690
  const runMinuteTick = () => {
65585
- void fetchBootstrap(true);
65691
+ requestScheduledForcedRefresh();
65586
65692
  };
65587
65693
  const startInterval = () => {
65588
65694
  runMinuteTick();
@@ -65601,7 +65707,7 @@ var useLiveMonitorBootstrap = ({
65601
65707
  window.clearInterval(intervalId);
65602
65708
  }
65603
65709
  };
65604
- }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
65710
+ }, [enabled, normalizedLineIds, requestScheduledForcedRefresh, resolvedCompanyId, supabase]);
65605
65711
  const isCurrentScopeResolved = state.requestKey === requestKey;
65606
65712
  return {
65607
65713
  resolvedScope: state.resolvedScope,
@@ -71578,6 +71684,7 @@ var KPIsOverviewView = ({
71578
71684
  const [viewType, setViewType] = React146.useState("operator");
71579
71685
  const [selectedLeaderboardDate, setSelectedLeaderboardDate] = React146.useState("");
71580
71686
  const [selectedLeaderboardShiftId, setSelectedLeaderboardShiftId] = React146.useState(0);
71687
+ const [leaderboardDailyScopeMode, setLeaderboardDailyScopeMode] = React146.useState("live");
71581
71688
  const [hasHydratedLeaderboardRouteState, setHasHydratedLeaderboardRouteState] = React146.useState(false);
71582
71689
  const [loading, setLoading] = React146.useState(true);
71583
71690
  const [isFilterOpen, setIsFilterOpen] = React146.useState(false);
@@ -71697,14 +71804,16 @@ var KPIsOverviewView = ({
71697
71804
  const currentShiftId = currentShiftDetails.shiftId;
71698
71805
  const activeFiltersCount = React146__namespace.default.useMemo(() => {
71699
71806
  let count = 0;
71700
- if (selectedLeaderboardShiftId !== currentShiftId) {
71807
+ if (leaderboardDailyScopeMode === "historical" && selectedLeaderboardShiftId !== currentShiftId) {
71701
71808
  count++;
71702
71809
  }
71703
71810
  return count;
71704
- }, [selectedLeaderboardShiftId, currentShiftId]);
71811
+ }, [leaderboardDailyScopeMode, selectedLeaderboardShiftId, currentShiftId]);
71705
71812
  const clearFilters = React146__namespace.default.useCallback(() => {
71706
71813
  setSelectedLeaderboardShiftId(currentShiftId);
71707
- }, [currentShiftId]);
71814
+ setSelectedLeaderboardDate(currentShiftDate);
71815
+ setLeaderboardDailyScopeMode("live");
71816
+ }, [currentShiftDate, currentShiftId]);
71708
71817
  React146.useEffect(() => {
71709
71818
  const handleClickOutside = (event) => {
71710
71819
  const target = event.target;
@@ -71736,7 +71845,24 @@ var KPIsOverviewView = ({
71736
71845
  }, [shiftConfig]);
71737
71846
  const effectiveLeaderboardDate = selectedLeaderboardDate || currentShiftDate;
71738
71847
  const effectiveLeaderboardShiftId = Number.isFinite(selectedLeaderboardShiftId) ? selectedLeaderboardShiftId : currentShiftId;
71739
- const isHistoricalLeaderboardDaily = activeTab === "leaderboard" && timeRange === "today" && (effectiveLeaderboardDate !== currentShiftDate || effectiveLeaderboardShiftId !== currentShiftId);
71848
+ const isHistoricalLeaderboardDaily = activeTab === "leaderboard" && timeRange === "today" && leaderboardDailyScopeMode === "historical" && (effectiveLeaderboardDate !== currentShiftDate || effectiveLeaderboardShiftId !== currentShiftId);
71849
+ const updateLeaderboardDate = React146__namespace.default.useCallback((dateKey) => {
71850
+ setSelectedLeaderboardDate(dateKey);
71851
+ setLeaderboardDailyScopeMode(
71852
+ dateKey === currentShiftDate && effectiveLeaderboardShiftId === currentShiftId ? "live" : "historical"
71853
+ );
71854
+ }, [currentShiftDate, currentShiftId, effectiveLeaderboardShiftId]);
71855
+ const updateLeaderboardShiftId = React146__namespace.default.useCallback((shiftId) => {
71856
+ setSelectedLeaderboardShiftId(shiftId);
71857
+ setLeaderboardDailyScopeMode(
71858
+ effectiveLeaderboardDate === currentShiftDate && shiftId === currentShiftId ? "live" : "historical"
71859
+ );
71860
+ }, [currentShiftDate, currentShiftId, effectiveLeaderboardDate]);
71861
+ const returnLeaderboardToLive = React146__namespace.default.useCallback(() => {
71862
+ setSelectedLeaderboardDate(currentShiftDate);
71863
+ setSelectedLeaderboardShiftId(currentShiftId);
71864
+ setLeaderboardDailyScopeMode("live");
71865
+ }, [currentShiftDate, currentShiftId]);
71740
71866
  const selectedFactoryIdFromUrl = getSingleQueryValue(router$1.query[KPI_FACTORY_QUERY_PARAM]);
71741
71867
  const selectedFactoryAreaIdFromUrl = getSingleQueryValue(router$1.query[KPI_FACTORY_AREA_QUERY_PARAM]);
71742
71868
  const kpiLineHierarchy = React146__namespace.default.useMemo(
@@ -71762,10 +71888,12 @@ var KPIsOverviewView = ({
71762
71888
  if (hasHistoricalQuery) {
71763
71889
  setSelectedLeaderboardDate(dateQuery);
71764
71890
  setSelectedLeaderboardShiftId(parsedShiftQuery);
71891
+ setLeaderboardDailyScopeMode("historical");
71765
71892
  setTimeRange("today");
71766
71893
  } else {
71767
71894
  setSelectedLeaderboardDate(currentShiftDate);
71768
71895
  setSelectedLeaderboardShiftId(currentShiftId);
71896
+ setLeaderboardDailyScopeMode("live");
71769
71897
  }
71770
71898
  setHasHydratedLeaderboardRouteState(true);
71771
71899
  }, [
@@ -71788,7 +71916,8 @@ var KPIsOverviewView = ({
71788
71916
  pendingTabRouteSyncRef.current = null;
71789
71917
  }
71790
71918
  const expectedTab = activeTab === "leaderboard" ? "leaderboard" : void 0;
71791
- const expectedDate = activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily ? effectiveLeaderboardDate : void 0;
71919
+ const shouldPersistHistoricalLeaderboardDaily = activeTab === "leaderboard" && timeRange === "today" && leaderboardDailyScopeMode === "historical" && isHistoricalLeaderboardDaily;
71920
+ const expectedDate = shouldPersistHistoricalLeaderboardDaily ? effectiveLeaderboardDate : void 0;
71792
71921
  const expectedShift = expectedDate !== void 0 ? effectiveLeaderboardShiftId.toString() : void 0;
71793
71922
  const expectedFactory = activeTab === "today" && selectedFactoryNode ? selectedFactoryNode.id : void 0;
71794
71923
  const expectedFactoryArea = activeTab === "today" && selectedFactoryNode && selectedFactoryAreaNode ? selectedFactoryAreaNode.id : void 0;
@@ -71814,6 +71943,7 @@ var KPIsOverviewView = ({
71814
71943
  router$1,
71815
71944
  activeTab,
71816
71945
  timeRange,
71946
+ leaderboardDailyScopeMode,
71817
71947
  effectiveLeaderboardDate,
71818
71948
  effectiveLeaderboardShiftId,
71819
71949
  hasHydratedLeaderboardRouteState,
@@ -72235,7 +72365,7 @@ var KPIsOverviewView = ({
72235
72365
  const currentEfficiencyMap = timeRange === "today" ? todayEfficiencyByLineId : monthlyEfficiencyByLineId;
72236
72366
  const currentFallbackEfficiencyMap = timeRange === "today" ? dailyFallbackEfficiencyByLineId : void 0;
72237
72367
  const showLeaderboardLoader = activeTab === "leaderboard" && (leaderboardLinesLoading || isLeaderboardLoading && currentEfficiencyMap.size === 0 && (currentFallbackEfficiencyMap?.size ?? 0) === 0);
72238
- const showHistoricalLeaderboardHeader = activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily;
72368
+ const showHistoricalLeaderboardHeader = activeTab === "leaderboard" && timeRange === "today" && leaderboardDailyScopeMode === "historical" && isHistoricalLeaderboardDaily;
72239
72369
  const headerDateKey = activeTab === "leaderboard" && timeRange === "today" ? effectiveLeaderboardDate : monthEndDateKey;
72240
72370
  const headerShiftId = showHistoricalLeaderboardHeader ? effectiveLeaderboardShiftId : currentShiftDetails.shiftId;
72241
72371
  const headerShiftName = getShiftNameById(headerShiftId, configuredTimezone, shiftConfig).replace(/ Shift$/i, "");
@@ -72430,10 +72560,7 @@ var KPIsOverviewView = ({
72430
72560
  "button",
72431
72561
  {
72432
72562
  type: "button",
72433
- onClick: () => {
72434
- setSelectedLeaderboardDate(currentShiftDate);
72435
- setSelectedLeaderboardShiftId(currentShiftId);
72436
- },
72563
+ onClick: returnLeaderboardToLive,
72437
72564
  className: "text-[10px] font-medium text-blue-600 hover:text-blue-700 hover:underline bg-blue-50 px-2 py-1 rounded-md",
72438
72565
  children: "Return to Live"
72439
72566
  }
@@ -72479,10 +72606,7 @@ var KPIsOverviewView = ({
72479
72606
  "button",
72480
72607
  {
72481
72608
  type: "button",
72482
- onClick: () => {
72483
- setSelectedLeaderboardDate(currentShiftDate);
72484
- setSelectedLeaderboardShiftId(currentShiftId);
72485
- },
72609
+ onClick: returnLeaderboardToLive,
72486
72610
  className: "text-xs sm:text-sm font-medium text-blue-600 bg-blue-50 border border-blue-100 hover:bg-blue-100 hover:text-blue-700 px-3 py-1.5 rounded-lg transition-colors shadow-sm ml-4 whitespace-nowrap",
72487
72611
  children: "Return to Live"
72488
72612
  }
@@ -72550,7 +72674,7 @@ var KPIsOverviewView = ({
72550
72674
  endKey: effectiveLeaderboardDate
72551
72675
  },
72552
72676
  onChange: (range) => {
72553
- setSelectedLeaderboardDate(range.startKey);
72677
+ updateLeaderboardDate(range.startKey);
72554
72678
  },
72555
72679
  showLabel: false,
72556
72680
  singleDateOnly: true
@@ -72590,7 +72714,7 @@ var KPIsOverviewView = ({
72590
72714
  {
72591
72715
  "aria-label": "Leaderboard shift",
72592
72716
  value: effectiveLeaderboardShiftId,
72593
- onChange: (e) => setSelectedLeaderboardShiftId(Number(e.target.value)),
72717
+ onChange: (e) => updateLeaderboardShiftId(Number(e.target.value)),
72594
72718
  className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
72595
72719
  style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.75rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
72596
72720
  children: leaderboardShiftOptions.map((shiftOption) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: shiftOption.id, children: shiftOption.label }, shiftOption.id))
package/dist/index.mjs CHANGED
@@ -37257,7 +37257,9 @@ var HourlyOutputChartComponent = ({
37257
37257
  const idleSlot = idleSlots[i];
37258
37258
  const explicitTarget = hasHourlyTargetOutputProp ? effectiveHourlyTargetOutput?.[i] ?? null : void 0;
37259
37259
  const currentTarget = hasHourlyTargetOutputProp ? explicitTarget : targets[i] || pphThreshold;
37260
+ const outputForComparison = Math.round(animatedData[i] || 0);
37260
37261
  const comparisonTarget = currentTarget === null || currentTarget === void 0 ? targets[i] || pphThreshold : currentTarget;
37262
+ const targetForComparison = Math.round(comparisonTarget);
37261
37263
  return {
37262
37264
  hourIndex: idleSlot?.hourIndex ?? i,
37263
37265
  hour: idleSlot?.hour || "",
@@ -37266,7 +37268,7 @@ var HourlyOutputChartComponent = ({
37266
37268
  originalOutput: data[i] || 0,
37267
37269
  // Keep original data for labels
37268
37270
  target: currentTarget ?? null,
37269
- color: (animatedData[i] || 0) >= comparisonTarget ? "#00AB45" : "#E34329",
37271
+ color: outputForComparison >= targetForComparison ? "#00AB45" : "#E34329",
37270
37272
  idleMinutes: idleSlot?.idleMinutes || 0,
37271
37273
  idleArray: idleSlot?.idleArray || [],
37272
37274
  skuIndex: i,
@@ -59636,6 +59638,42 @@ var buildSnapshotRows = (entries, lines) => {
59636
59638
  rowType: row.rowType
59637
59639
  }));
59638
59640
  };
59641
+ var isValidSnapshotRow = (row) => !!row && typeof row.efficiency === "number" && Number.isFinite(row.efficiency) && typeof row.rank === "number" && Number.isFinite(row.rank);
59642
+ var refreshPendingEventsFromSnapshot = (events, currentSnapshot) => {
59643
+ if (events.length === 0 || currentSnapshot.length === 0) return [];
59644
+ const snapshotByRowId = new Map(currentSnapshot.map((row) => [row.lineId, row]));
59645
+ return events.flatMap((event) => {
59646
+ const currentSubject = snapshotByRowId.get(event.crossingLineId);
59647
+ if (!isValidSnapshotRow(currentSubject)) return [];
59648
+ const movedFromOriginalRank = event.direction === "up" ? currentSubject.rank < event.previousRank : currentSubject.rank > event.previousRank;
59649
+ if (!movedFromOriginalRank) return [];
59650
+ const crossedLines = event.crossedLines.flatMap((crossedLine) => {
59651
+ const currentCrossedLine = snapshotByRowId.get(crossedLine.lineId);
59652
+ if (!isValidSnapshotRow(currentCrossedLine)) return [];
59653
+ const stillCrossed = event.direction === "up" ? currentSubject.rank < currentCrossedLine.rank : currentSubject.rank > currentCrossedLine.rank;
59654
+ if (!stillCrossed) return [];
59655
+ return [{
59656
+ ...crossedLine,
59657
+ lineName: currentCrossedLine.lineName,
59658
+ currentRank: currentCrossedLine.rank,
59659
+ currentEfficiency: currentCrossedLine.efficiency
59660
+ }];
59661
+ });
59662
+ if (crossedLines.length !== event.crossedLines.length) return [];
59663
+ return [{
59664
+ ...event,
59665
+ crossingLineName: currentSubject.lineName,
59666
+ currentRank: currentSubject.rank,
59667
+ currentEfficiency: currentSubject.efficiency,
59668
+ crossedLines,
59669
+ eventKey: buildLineOvertakeEventKey(
59670
+ event.crossingLineId,
59671
+ event.direction,
59672
+ crossedLines.map((line) => line.lineId)
59673
+ )
59674
+ }];
59675
+ });
59676
+ };
59639
59677
  var formatCrossedLineNames = (event) => {
59640
59678
  const names = event.crossedLines.map((line) => line.lineName).filter(Boolean);
59641
59679
  if (names.length === 0) return "another line";
@@ -59693,6 +59731,7 @@ var storeEventKey = (scopeKey, eventKey) => {
59693
59731
  } catch {
59694
59732
  }
59695
59733
  };
59734
+ var buildAcknowledgementKey = (scopeKey, eventKey) => `${scopeKey}:${eventKey}`;
59696
59735
  var LineOvertakeNotificationPopup = ({
59697
59736
  events,
59698
59737
  onDismiss,
@@ -59842,6 +59881,7 @@ var LineOvertakeNotificationManager = ({
59842
59881
  const [visibleLines, setVisibleLines] = useState([]);
59843
59882
  const [linesLoaded, setLinesLoaded] = useState(false);
59844
59883
  const [notificationEvents, setNotificationEvents] = useState([]);
59884
+ const [returnValidationPending, setReturnValidationPending] = useState(false);
59845
59885
  const isOnActiveToastSurface = isActiveToastSurface(router.asPath);
59846
59886
  const qaSimulationMode = isOnActiveToastSurface ? getLocalQaSimulationMode(router.asPath) : null;
59847
59887
  const qaSimulationEnabled = Boolean(qaSimulationMode);
@@ -59868,20 +59908,33 @@ var LineOvertakeNotificationManager = ({
59868
59908
  const scopeKeyRef = useRef("");
59869
59909
  const pollInFlightRef = useRef(false);
59870
59910
  const notificationEventsRef = useRef([]);
59911
+ const acknowledgedEventKeysRef = useRef(/* @__PURE__ */ new Set());
59912
+ const wasOnActiveToastSurfaceRef = useRef(isOnActiveToastSurface);
59871
59913
  const dedupeScopeKey = useMemo(
59872
- () => [
59873
- user?.id || "anonymous",
59874
- companyId || "no-company",
59875
- currentShift.date,
59876
- currentShift.shiftId,
59877
- roleMode || "disabled",
59878
- watchedLineIds.slice().sort().join(",")
59879
- ].join("|"),
59914
+ () => {
59915
+ if (qaSimulationEnabled) {
59916
+ return [
59917
+ user?.id || "anonymous",
59918
+ "qa-simulation",
59919
+ qaSimulationMode || "default"
59920
+ ].join("|");
59921
+ }
59922
+ return [
59923
+ user?.id || "anonymous",
59924
+ companyId || "no-company",
59925
+ currentShift.date,
59926
+ currentShift.shiftId,
59927
+ roleMode || "disabled",
59928
+ watchedLineIds.slice().sort().join(",")
59929
+ ].join("|");
59930
+ },
59880
59931
  [
59881
59932
  user?.id,
59882
59933
  companyId,
59883
59934
  currentShift.date,
59884
59935
  currentShift.shiftId,
59936
+ qaSimulationEnabled,
59937
+ qaSimulationMode,
59885
59938
  roleMode,
59886
59939
  watchedLineIds
59887
59940
  ]
@@ -59918,13 +59971,45 @@ var LineOvertakeNotificationManager = ({
59918
59971
  previousSnapshotRef.current = null;
59919
59972
  notificationEventsRef.current = [];
59920
59973
  setNotificationEvents([]);
59974
+ setReturnValidationPending(false);
59921
59975
  scopeKeyRef.current = scopeKey;
59922
59976
  }, [isOnActiveToastSurface, scopeKey]);
59923
59977
  useEffect(() => {
59924
- if (isOnActiveToastSurface) return;
59978
+ const wasOnActiveToastSurface = wasOnActiveToastSurfaceRef.current;
59979
+ wasOnActiveToastSurfaceRef.current = isOnActiveToastSurface;
59980
+ if (wasOnActiveToastSurface && !isOnActiveToastSurface && notificationEventsRef.current.length > 0) {
59981
+ setReturnValidationPending(true);
59982
+ return;
59983
+ }
59984
+ if (!wasOnActiveToastSurface && isOnActiveToastSurface && notificationEventsRef.current.length === 0) {
59985
+ setReturnValidationPending(false);
59986
+ }
59987
+ }, [isOnActiveToastSurface]);
59988
+ const acknowledgeCurrentEvents = useCallback(() => {
59989
+ notificationEventsRef.current.forEach((event) => {
59990
+ acknowledgedEventKeysRef.current.add(buildAcknowledgementKey(dedupeScopeKey, event.eventKey));
59991
+ storeEventKey(dedupeScopeKey, event.eventKey);
59992
+ });
59925
59993
  notificationEventsRef.current = [];
59926
59994
  setNotificationEvents([]);
59927
- }, [isOnActiveToastSurface]);
59995
+ setReturnValidationPending(false);
59996
+ }, [dedupeScopeKey]);
59997
+ const clearPendingEvents = useCallback(() => {
59998
+ notificationEventsRef.current = [];
59999
+ setNotificationEvents([]);
60000
+ setReturnValidationPending(false);
60001
+ }, []);
60002
+ const finishReturnValidation = useCallback((events, currentSnapshot) => {
60003
+ if (events.length === 0 && notificationEventsRef.current.length > 0) {
60004
+ const validatedEvents = refreshPendingEventsFromSnapshot(
60005
+ notificationEventsRef.current,
60006
+ currentSnapshot
60007
+ );
60008
+ notificationEventsRef.current = validatedEvents;
60009
+ setNotificationEvents(validatedEvents);
60010
+ }
60011
+ setReturnValidationPending(false);
60012
+ }, []);
59928
60013
  useEffect(() => {
59929
60014
  let cancelled = false;
59930
60015
  const loadLines = async () => {
@@ -59965,10 +60050,9 @@ var LineOvertakeNotificationManager = ({
59965
60050
  };
59966
60051
  }, [companyId, isOnActiveToastSurface, qaSimulationEnabled, roleMode, supabase]);
59967
60052
  const openLeaderboard = useCallback(() => {
59968
- notificationEventsRef.current = [];
59969
- setNotificationEvents([]);
60053
+ acknowledgeCurrentEvents();
59970
60054
  void router.push("/kpis?tab=leaderboard");
59971
- }, [router]);
60055
+ }, [acknowledgeCurrentEvents, router]);
59972
60056
  const updateOvertakeEvents = useCallback((events) => {
59973
60057
  if (events.length === 0) return;
59974
60058
  const currentEventByRowId = new Map(
@@ -59976,13 +60060,9 @@ var LineOvertakeNotificationManager = ({
59976
60060
  );
59977
60061
  const nextEvents = events.filter((event) => {
59978
60062
  if (currentEventByRowId.has(event.crossingLineId)) return true;
59979
- return !hasStoredEventKey(dedupeScopeKey, event.eventKey);
60063
+ return !(acknowledgedEventKeysRef.current.has(buildAcknowledgementKey(dedupeScopeKey, event.eventKey)) || hasStoredEventKey(dedupeScopeKey, event.eventKey));
59980
60064
  });
59981
60065
  if (nextEvents.length === 0) return;
59982
- nextEvents.forEach((event) => {
59983
- if (currentEventByRowId.has(event.crossingLineId)) return;
59984
- storeEventKey(dedupeScopeKey, event.eventKey);
59985
- });
59986
60066
  setNotificationEvents((currentEvents) => {
59987
60067
  const eventByRowId = new Map(currentEvents.map((event) => [event.crossingLineId, event]));
59988
60068
  nextEvents.forEach((event) => {
@@ -60025,6 +60105,7 @@ var LineOvertakeNotificationManager = ({
60025
60105
  });
60026
60106
  previousSnapshotRef.current = QA_CROSSED_ROWS;
60027
60107
  updateOvertakeEvents(events);
60108
+ finishReturnValidation(events, QA_CROSSED_ROWS);
60028
60109
  } finally {
60029
60110
  pollInFlightRef.current = false;
60030
60111
  }
@@ -60047,11 +60128,17 @@ var LineOvertakeNotificationManager = ({
60047
60128
  const currentSnapshot = buildSnapshotRows(entries, visibleLines);
60048
60129
  if (currentSnapshot.length === 0) {
60049
60130
  previousSnapshotRef.current = null;
60131
+ if (returnValidationPending) {
60132
+ clearPendingEvents();
60133
+ }
60050
60134
  return;
60051
60135
  }
60052
60136
  const previousSnapshot = previousSnapshotRef.current;
60053
60137
  previousSnapshotRef.current = currentSnapshot;
60054
- if (!previousSnapshot) return;
60138
+ if (!previousSnapshot) {
60139
+ finishReturnValidation([], currentSnapshot);
60140
+ return;
60141
+ }
60055
60142
  const events = detectLineOvertakeEvents({
60056
60143
  roleMode,
60057
60144
  watchedLineIds,
@@ -60059,6 +60146,7 @@ var LineOvertakeNotificationManager = ({
60059
60146
  currentRows: currentSnapshot
60060
60147
  });
60061
60148
  updateOvertakeEvents(events);
60149
+ finishReturnValidation(events, currentSnapshot);
60062
60150
  } catch (error) {
60063
60151
  console.warn("[LineOvertakeNotificationManager] Failed to poll daily leaderboard:", error);
60064
60152
  } finally {
@@ -60072,8 +60160,11 @@ var LineOvertakeNotificationManager = ({
60072
60160
  linesLoaded,
60073
60161
  qaSimulationEnabled,
60074
60162
  qaSimulationMode,
60163
+ returnValidationPending,
60075
60164
  roleMode,
60076
60165
  supabase,
60166
+ clearPendingEvents,
60167
+ finishReturnValidation,
60077
60168
  updateOvertakeEvents,
60078
60169
  visibleLineIds,
60079
60170
  watchedLineIds
@@ -60091,14 +60182,12 @@ var LineOvertakeNotificationManager = ({
60091
60182
  };
60092
60183
  }, [pollIntervalMs, pollLeaderboard]);
60093
60184
  if (!isOnActiveToastSurface) return null;
60185
+ if (returnValidationPending) return null;
60094
60186
  return /* @__PURE__ */ jsx(
60095
60187
  LineOvertakeNotificationPopup,
60096
60188
  {
60097
60189
  events: notificationEvents,
60098
- onDismiss: () => {
60099
- notificationEventsRef.current = [];
60100
- setNotificationEvents([]);
60101
- },
60190
+ onDismiss: acknowledgeCurrentEvents,
60102
60191
  onOpenLeaderboard: openLeaderboard
60103
60192
  }
60104
60193
  );
@@ -65276,6 +65365,7 @@ var useLiveMonitorBootstrap = ({
65276
65365
  const activeRequestIdRef = useRef(0);
65277
65366
  const isFetchingRef = useRef(false);
65278
65367
  const pendingRealtimeRefreshRef = useRef(false);
65368
+ const pendingForcedRefreshRef = useRef(false);
65279
65369
  const realtimeRefreshTimerRef = useRef(null);
65280
65370
  const lastRealtimeRefreshStartedAtRef = useRef(0);
65281
65371
  const fetchBootstrap = useCallback(async (force = false) => {
@@ -65349,6 +65439,18 @@ var useLiveMonitorBootstrap = ({
65349
65439
  useEffect(() => {
65350
65440
  fetchBootstrapRef.current = fetchBootstrap;
65351
65441
  }, [fetchBootstrap]);
65442
+ const requestScheduledForcedRefresh = useCallback(() => {
65443
+ if (!enabled || !supabase || !resolvedCompanyId || normalizedLineIds.length === 0) {
65444
+ pendingForcedRefreshRef.current = false;
65445
+ return;
65446
+ }
65447
+ if (isFetchingRef.current) {
65448
+ pendingForcedRefreshRef.current = true;
65449
+ return;
65450
+ }
65451
+ pendingForcedRefreshRef.current = false;
65452
+ void fetchBootstrapRef.current(true);
65453
+ }, [enabled, normalizedLineIds.length, resolvedCompanyId, supabase]);
65352
65454
  const clearRealtimeRefreshTimer = useCallback(() => {
65353
65455
  if (realtimeRefreshTimerRef.current !== null) {
65354
65456
  window.clearTimeout(realtimeRefreshTimerRef.current);
@@ -65373,9 +65475,9 @@ var useLiveMonitorBootstrap = ({
65373
65475
  }
65374
65476
  pendingRealtimeRefreshRef.current = false;
65375
65477
  lastRealtimeRefreshStartedAtRef.current = Date.now();
65376
- void fetchBootstrapRef.current(true);
65478
+ requestScheduledForcedRefresh();
65377
65479
  }, nextDelay);
65378
- }, [enabled, supabase]);
65480
+ }, [enabled, requestScheduledForcedRefresh, supabase]);
65379
65481
  const queueRealtimeRefresh = useCallback(() => {
65380
65482
  if (!enabled || !supabase) {
65381
65483
  pendingRealtimeRefreshRef.current = false;
@@ -65388,9 +65490,15 @@ var useLiveMonitorBootstrap = ({
65388
65490
  useEffect(() => {
65389
65491
  return () => {
65390
65492
  pendingRealtimeRefreshRef.current = false;
65493
+ pendingForcedRefreshRef.current = false;
65391
65494
  clearRealtimeRefreshTimer();
65392
65495
  };
65393
65496
  }, [clearRealtimeRefreshTimer]);
65497
+ useEffect(() => {
65498
+ if (!isLoading && pendingForcedRefreshRef.current) {
65499
+ requestScheduledForcedRefresh();
65500
+ }
65501
+ }, [isLoading, requestScheduledForcedRefresh]);
65394
65502
  useEffect(() => {
65395
65503
  if (!enabled || !rawState || !resolvedCompanyId) {
65396
65504
  return;
@@ -65529,9 +65637,7 @@ var useLiveMonitorBootstrap = ({
65529
65637
  if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
65530
65638
  return void 0;
65531
65639
  }
65532
- const forceRefreshOnResume = () => {
65533
- void fetchBootstrapRef.current(true);
65534
- };
65640
+ const forceRefreshOnResume = () => requestScheduledForcedRefresh();
65535
65641
  const handleVisibilityChange = () => {
65536
65642
  if (document.visibilityState === "visible") {
65537
65643
  forceRefreshOnResume();
@@ -65545,7 +65651,7 @@ var useLiveMonitorBootstrap = ({
65545
65651
  window.removeEventListener("focus", forceRefreshOnResume);
65546
65652
  window.removeEventListener("online", forceRefreshOnResume);
65547
65653
  };
65548
- }, [enabled, normalizedLineIds.length, resolvedCompanyId, supabase]);
65654
+ }, [enabled, normalizedLineIds.length, requestScheduledForcedRefresh, resolvedCompanyId, supabase]);
65549
65655
  useEffect(() => {
65550
65656
  if (!enabled || !resolvedCompanyId || normalizedLineIds.length === 0 || !supabase) {
65551
65657
  return void 0;
@@ -65553,7 +65659,7 @@ var useLiveMonitorBootstrap = ({
65553
65659
  let intervalId = null;
65554
65660
  let timeoutId = null;
65555
65661
  const runMinuteTick = () => {
65556
- void fetchBootstrap(true);
65662
+ requestScheduledForcedRefresh();
65557
65663
  };
65558
65664
  const startInterval = () => {
65559
65665
  runMinuteTick();
@@ -65572,7 +65678,7 @@ var useLiveMonitorBootstrap = ({
65572
65678
  window.clearInterval(intervalId);
65573
65679
  }
65574
65680
  };
65575
- }, [enabled, resolvedCompanyId, normalizedLineIds, supabase, fetchBootstrap]);
65681
+ }, [enabled, normalizedLineIds, requestScheduledForcedRefresh, resolvedCompanyId, supabase]);
65576
65682
  const isCurrentScopeResolved = state.requestKey === requestKey;
65577
65683
  return {
65578
65684
  resolvedScope: state.resolvedScope,
@@ -71549,6 +71655,7 @@ var KPIsOverviewView = ({
71549
71655
  const [viewType, setViewType] = useState("operator");
71550
71656
  const [selectedLeaderboardDate, setSelectedLeaderboardDate] = useState("");
71551
71657
  const [selectedLeaderboardShiftId, setSelectedLeaderboardShiftId] = useState(0);
71658
+ const [leaderboardDailyScopeMode, setLeaderboardDailyScopeMode] = useState("live");
71552
71659
  const [hasHydratedLeaderboardRouteState, setHasHydratedLeaderboardRouteState] = useState(false);
71553
71660
  const [loading, setLoading] = useState(true);
71554
71661
  const [isFilterOpen, setIsFilterOpen] = useState(false);
@@ -71668,14 +71775,16 @@ var KPIsOverviewView = ({
71668
71775
  const currentShiftId = currentShiftDetails.shiftId;
71669
71776
  const activeFiltersCount = React146__default.useMemo(() => {
71670
71777
  let count = 0;
71671
- if (selectedLeaderboardShiftId !== currentShiftId) {
71778
+ if (leaderboardDailyScopeMode === "historical" && selectedLeaderboardShiftId !== currentShiftId) {
71672
71779
  count++;
71673
71780
  }
71674
71781
  return count;
71675
- }, [selectedLeaderboardShiftId, currentShiftId]);
71782
+ }, [leaderboardDailyScopeMode, selectedLeaderboardShiftId, currentShiftId]);
71676
71783
  const clearFilters = React146__default.useCallback(() => {
71677
71784
  setSelectedLeaderboardShiftId(currentShiftId);
71678
- }, [currentShiftId]);
71785
+ setSelectedLeaderboardDate(currentShiftDate);
71786
+ setLeaderboardDailyScopeMode("live");
71787
+ }, [currentShiftDate, currentShiftId]);
71679
71788
  useEffect(() => {
71680
71789
  const handleClickOutside = (event) => {
71681
71790
  const target = event.target;
@@ -71707,7 +71816,24 @@ var KPIsOverviewView = ({
71707
71816
  }, [shiftConfig]);
71708
71817
  const effectiveLeaderboardDate = selectedLeaderboardDate || currentShiftDate;
71709
71818
  const effectiveLeaderboardShiftId = Number.isFinite(selectedLeaderboardShiftId) ? selectedLeaderboardShiftId : currentShiftId;
71710
- const isHistoricalLeaderboardDaily = activeTab === "leaderboard" && timeRange === "today" && (effectiveLeaderboardDate !== currentShiftDate || effectiveLeaderboardShiftId !== currentShiftId);
71819
+ const isHistoricalLeaderboardDaily = activeTab === "leaderboard" && timeRange === "today" && leaderboardDailyScopeMode === "historical" && (effectiveLeaderboardDate !== currentShiftDate || effectiveLeaderboardShiftId !== currentShiftId);
71820
+ const updateLeaderboardDate = React146__default.useCallback((dateKey) => {
71821
+ setSelectedLeaderboardDate(dateKey);
71822
+ setLeaderboardDailyScopeMode(
71823
+ dateKey === currentShiftDate && effectiveLeaderboardShiftId === currentShiftId ? "live" : "historical"
71824
+ );
71825
+ }, [currentShiftDate, currentShiftId, effectiveLeaderboardShiftId]);
71826
+ const updateLeaderboardShiftId = React146__default.useCallback((shiftId) => {
71827
+ setSelectedLeaderboardShiftId(shiftId);
71828
+ setLeaderboardDailyScopeMode(
71829
+ effectiveLeaderboardDate === currentShiftDate && shiftId === currentShiftId ? "live" : "historical"
71830
+ );
71831
+ }, [currentShiftDate, currentShiftId, effectiveLeaderboardDate]);
71832
+ const returnLeaderboardToLive = React146__default.useCallback(() => {
71833
+ setSelectedLeaderboardDate(currentShiftDate);
71834
+ setSelectedLeaderboardShiftId(currentShiftId);
71835
+ setLeaderboardDailyScopeMode("live");
71836
+ }, [currentShiftDate, currentShiftId]);
71711
71837
  const selectedFactoryIdFromUrl = getSingleQueryValue(router.query[KPI_FACTORY_QUERY_PARAM]);
71712
71838
  const selectedFactoryAreaIdFromUrl = getSingleQueryValue(router.query[KPI_FACTORY_AREA_QUERY_PARAM]);
71713
71839
  const kpiLineHierarchy = React146__default.useMemo(
@@ -71733,10 +71859,12 @@ var KPIsOverviewView = ({
71733
71859
  if (hasHistoricalQuery) {
71734
71860
  setSelectedLeaderboardDate(dateQuery);
71735
71861
  setSelectedLeaderboardShiftId(parsedShiftQuery);
71862
+ setLeaderboardDailyScopeMode("historical");
71736
71863
  setTimeRange("today");
71737
71864
  } else {
71738
71865
  setSelectedLeaderboardDate(currentShiftDate);
71739
71866
  setSelectedLeaderboardShiftId(currentShiftId);
71867
+ setLeaderboardDailyScopeMode("live");
71740
71868
  }
71741
71869
  setHasHydratedLeaderboardRouteState(true);
71742
71870
  }, [
@@ -71759,7 +71887,8 @@ var KPIsOverviewView = ({
71759
71887
  pendingTabRouteSyncRef.current = null;
71760
71888
  }
71761
71889
  const expectedTab = activeTab === "leaderboard" ? "leaderboard" : void 0;
71762
- const expectedDate = activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily ? effectiveLeaderboardDate : void 0;
71890
+ const shouldPersistHistoricalLeaderboardDaily = activeTab === "leaderboard" && timeRange === "today" && leaderboardDailyScopeMode === "historical" && isHistoricalLeaderboardDaily;
71891
+ const expectedDate = shouldPersistHistoricalLeaderboardDaily ? effectiveLeaderboardDate : void 0;
71763
71892
  const expectedShift = expectedDate !== void 0 ? effectiveLeaderboardShiftId.toString() : void 0;
71764
71893
  const expectedFactory = activeTab === "today" && selectedFactoryNode ? selectedFactoryNode.id : void 0;
71765
71894
  const expectedFactoryArea = activeTab === "today" && selectedFactoryNode && selectedFactoryAreaNode ? selectedFactoryAreaNode.id : void 0;
@@ -71785,6 +71914,7 @@ var KPIsOverviewView = ({
71785
71914
  router,
71786
71915
  activeTab,
71787
71916
  timeRange,
71917
+ leaderboardDailyScopeMode,
71788
71918
  effectiveLeaderboardDate,
71789
71919
  effectiveLeaderboardShiftId,
71790
71920
  hasHydratedLeaderboardRouteState,
@@ -72206,7 +72336,7 @@ var KPIsOverviewView = ({
72206
72336
  const currentEfficiencyMap = timeRange === "today" ? todayEfficiencyByLineId : monthlyEfficiencyByLineId;
72207
72337
  const currentFallbackEfficiencyMap = timeRange === "today" ? dailyFallbackEfficiencyByLineId : void 0;
72208
72338
  const showLeaderboardLoader = activeTab === "leaderboard" && (leaderboardLinesLoading || isLeaderboardLoading && currentEfficiencyMap.size === 0 && (currentFallbackEfficiencyMap?.size ?? 0) === 0);
72209
- const showHistoricalLeaderboardHeader = activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily;
72339
+ const showHistoricalLeaderboardHeader = activeTab === "leaderboard" && timeRange === "today" && leaderboardDailyScopeMode === "historical" && isHistoricalLeaderboardDaily;
72210
72340
  const headerDateKey = activeTab === "leaderboard" && timeRange === "today" ? effectiveLeaderboardDate : monthEndDateKey;
72211
72341
  const headerShiftId = showHistoricalLeaderboardHeader ? effectiveLeaderboardShiftId : currentShiftDetails.shiftId;
72212
72342
  const headerShiftName = getShiftNameById(headerShiftId, configuredTimezone, shiftConfig).replace(/ Shift$/i, "");
@@ -72401,10 +72531,7 @@ var KPIsOverviewView = ({
72401
72531
  "button",
72402
72532
  {
72403
72533
  type: "button",
72404
- onClick: () => {
72405
- setSelectedLeaderboardDate(currentShiftDate);
72406
- setSelectedLeaderboardShiftId(currentShiftId);
72407
- },
72534
+ onClick: returnLeaderboardToLive,
72408
72535
  className: "text-[10px] font-medium text-blue-600 hover:text-blue-700 hover:underline bg-blue-50 px-2 py-1 rounded-md",
72409
72536
  children: "Return to Live"
72410
72537
  }
@@ -72450,10 +72577,7 @@ var KPIsOverviewView = ({
72450
72577
  "button",
72451
72578
  {
72452
72579
  type: "button",
72453
- onClick: () => {
72454
- setSelectedLeaderboardDate(currentShiftDate);
72455
- setSelectedLeaderboardShiftId(currentShiftId);
72456
- },
72580
+ onClick: returnLeaderboardToLive,
72457
72581
  className: "text-xs sm:text-sm font-medium text-blue-600 bg-blue-50 border border-blue-100 hover:bg-blue-100 hover:text-blue-700 px-3 py-1.5 rounded-lg transition-colors shadow-sm ml-4 whitespace-nowrap",
72458
72582
  children: "Return to Live"
72459
72583
  }
@@ -72521,7 +72645,7 @@ var KPIsOverviewView = ({
72521
72645
  endKey: effectiveLeaderboardDate
72522
72646
  },
72523
72647
  onChange: (range) => {
72524
- setSelectedLeaderboardDate(range.startKey);
72648
+ updateLeaderboardDate(range.startKey);
72525
72649
  },
72526
72650
  showLabel: false,
72527
72651
  singleDateOnly: true
@@ -72561,7 +72685,7 @@ var KPIsOverviewView = ({
72561
72685
  {
72562
72686
  "aria-label": "Leaderboard shift",
72563
72687
  value: effectiveLeaderboardShiftId,
72564
- onChange: (e) => setSelectedLeaderboardShiftId(Number(e.target.value)),
72688
+ onChange: (e) => updateLeaderboardShiftId(Number(e.target.value)),
72565
72689
  className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
72566
72690
  style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.75rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
72567
72691
  children: leaderboardShiftOptions.map((shiftOption) => /* @__PURE__ */ jsx("option", { value: shiftOption.id, children: shiftOption.label }, shiftOption.id))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.12.38",
3
+ "version": "6.12.39",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",