@optifye/dashboard-core 6.0.9 → 6.1.0

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
@@ -5016,34 +5016,6 @@ var useWorkspaceOperators = (workspaceId, options) => {
5016
5016
  refetch: fetchData
5017
5017
  };
5018
5018
  };
5019
-
5020
- // src/lib/utils/dashboardReload.ts
5021
- var createThrottledReload = (interval = 5e3) => {
5022
- let last = 0;
5023
- let queued = false;
5024
- const doReload = () => {
5025
- if (typeof window !== "undefined") {
5026
- window.location.reload();
5027
- }
5028
- };
5029
- return () => {
5030
- const now2 = Date.now();
5031
- if (now2 - last >= interval) {
5032
- last = now2;
5033
- doReload();
5034
- } else if (!queued) {
5035
- queued = true;
5036
- setTimeout(() => {
5037
- queued = false;
5038
- last = Date.now();
5039
- doReload();
5040
- }, interval - (now2 - last));
5041
- }
5042
- };
5043
- };
5044
- var throttledReloadDashboard = createThrottledReload(5e3);
5045
-
5046
- // src/lib/hooks/useHlsStream.ts
5047
5019
  var HLS_CONFIG = {
5048
5020
  maxBufferLength: 8,
5049
5021
  maxMaxBufferLength: 15,
@@ -5062,7 +5034,35 @@ var HLS_CONFIG = {
5062
5034
  liveSyncDurationCount: 2
5063
5035
  // Follow live edge aggressively
5064
5036
  };
5037
+ var failedUrls = /* @__PURE__ */ new Map();
5038
+ var MAX_FAILURES_PER_URL = 3;
5039
+ var FAILURE_EXPIRY_MS = 5 * 60 * 1e3;
5040
+ function resetFailedUrl(url) {
5041
+ if (failedUrls.has(url)) {
5042
+ console.log(`[HLS] Manually resetting failed URL: ${url}`);
5043
+ failedUrls.delete(url);
5044
+ }
5045
+ }
5046
+ function isUrlPermanentlyFailed(url) {
5047
+ const failure = failedUrls.get(url);
5048
+ return failure?.permanentlyFailed || false;
5049
+ }
5065
5050
  function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5051
+ const urlFailure = failedUrls.get(src);
5052
+ const isPermanentlyFailed = urlFailure?.permanentlyFailed || false;
5053
+ const cleanupFailedUrls = () => {
5054
+ const now2 = Date.now();
5055
+ const expiredUrls = [];
5056
+ failedUrls.forEach((failure, url) => {
5057
+ if (now2 - failure.timestamp > FAILURE_EXPIRY_MS) {
5058
+ expiredUrls.push(url);
5059
+ }
5060
+ });
5061
+ expiredUrls.forEach((url) => {
5062
+ console.log(`[HLS] Removing expired failure entry for: ${url}`);
5063
+ failedUrls.delete(url);
5064
+ });
5065
+ };
5066
5066
  const [restartKey, setRestartKey] = React19.useState(0);
5067
5067
  const hlsRef = React19.useRef(null);
5068
5068
  const stallCheckIntervalRef = React19.useRef(null);
@@ -5132,14 +5132,33 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5132
5132
  };
5133
5133
  const hardRestart = (reason) => {
5134
5134
  console.warn(`[HLS] Hard restart: ${reason}`);
5135
+ cleanupFailedUrls();
5136
+ const failure = failedUrls.get(src);
5137
+ const failureCount = failure ? failure.count : 0;
5138
+ if (failureCount >= MAX_FAILURES_PER_URL) {
5139
+ console.error(`[HLS] URL has failed ${failureCount} times. Marking as permanently failed: ${src}`);
5140
+ failedUrls.set(src, {
5141
+ count: failureCount,
5142
+ timestamp: Date.now(),
5143
+ permanentlyFailed: true
5144
+ });
5145
+ cleanup();
5146
+ if (onFatalError) {
5147
+ onFatalError();
5148
+ }
5149
+ return;
5150
+ }
5151
+ failedUrls.set(src, {
5152
+ count: failureCount + 1,
5153
+ timestamp: Date.now(),
5154
+ permanentlyFailed: false
5155
+ });
5135
5156
  cleanup();
5136
5157
  setRestartKey((k) => k + 1);
5137
5158
  softRestartCountRef.current = 0;
5138
- if (reason.includes("hard restart") || reason.includes("native video error")) {
5159
+ if (reason.includes("404 hard restart")) {
5139
5160
  if (onFatalError) {
5140
5161
  onFatalError();
5141
- } else {
5142
- throttledReloadDashboard();
5143
5162
  }
5144
5163
  }
5145
5164
  };
@@ -5193,6 +5212,13 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5193
5212
  }, 7e3);
5194
5213
  };
5195
5214
  React19.useEffect(() => {
5215
+ if (isPermanentlyFailed) {
5216
+ console.warn(`[HLS] URL is permanently failed, not attempting to load: ${src}`);
5217
+ if (onFatalError) {
5218
+ onFatalError();
5219
+ }
5220
+ return;
5221
+ }
5196
5222
  if (!src || !shouldPlay) {
5197
5223
  cleanup();
5198
5224
  return;
@@ -5223,6 +5249,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5223
5249
  }
5224
5250
  });
5225
5251
  hls.on(Hls2__default.default.Events.MANIFEST_PARSED, () => {
5252
+ if (failedUrls.has(src)) {
5253
+ console.log(`[HLS] Stream loaded successfully, resetting failure count for: ${src}`);
5254
+ failedUrls.delete(src);
5255
+ }
5226
5256
  video.play().catch((err) => {
5227
5257
  console.error("[HLS] Play failed:", err);
5228
5258
  });
@@ -5241,7 +5271,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5241
5271
  console.error("[HLS] HLS not supported");
5242
5272
  }
5243
5273
  return cleanup;
5244
- }, [src, shouldPlay, restartKey, onFatalError]);
5274
+ }, [src, shouldPlay, restartKey, onFatalError, isPermanentlyFailed]);
5245
5275
  return {
5246
5276
  restartKey,
5247
5277
  isNativeHls: isNativeHlsRef.current
@@ -6346,7 +6376,7 @@ function useNavigation(customNavigate) {
6346
6376
  }
6347
6377
  function useWorkspaceNavigation() {
6348
6378
  const { defaultTimezone } = useDateTimeConfig();
6349
- const getWorkspaceNavigationParams3 = React19.useCallback(
6379
+ const getWorkspaceNavigationParams2 = React19.useCallback(
6350
6380
  (workspaceId, options) => {
6351
6381
  let dateToUse = options?.date;
6352
6382
  if (!dateToUse && options?.useCurrentDate) {
@@ -6362,7 +6392,7 @@ function useWorkspaceNavigation() {
6362
6392
  [defaultTimezone]
6363
6393
  );
6364
6394
  return {
6365
- getWorkspaceNavigationParams: getWorkspaceNavigationParams3
6395
+ getWorkspaceNavigationParams: getWorkspaceNavigationParams2
6366
6396
  };
6367
6397
  }
6368
6398
  function useDateFormatter() {
@@ -9060,9 +9090,15 @@ var getStoredWorkspaceMappings = () => {
9060
9090
  var getDefaultTabForWorkspace = (workspaceId, displayName) => {
9061
9091
  return "overview";
9062
9092
  };
9063
- var getWorkspaceNavigationParams = (workspaceId, displayName) => {
9093
+ var getWorkspaceNavigationParams = (workspaceId, displayName, lineId) => {
9064
9094
  const defaultTab = getDefaultTabForWorkspace();
9065
- return `?displayName=${encodeURIComponent(displayName)}&defaultTab=${defaultTab}`;
9095
+ const params = new URLSearchParams();
9096
+ params.set("displayName", displayName);
9097
+ params.set("defaultTab", defaultTab);
9098
+ if (lineId) {
9099
+ params.set("lineId", lineId);
9100
+ }
9101
+ return `?${params.toString()}`;
9066
9102
  };
9067
9103
 
9068
9104
  // src/lib/utils/videoPreloader.ts
@@ -9383,6 +9419,115 @@ var clearS3VideoCache = () => {
9383
9419
  var preloadS3VideoUrl = s3VideoPreloader.preloadVideo;
9384
9420
  var preloadS3VideosUrl = s3VideoPreloader.preloadVideos;
9385
9421
 
9422
+ // src/lib/utils/dashboardReload.ts
9423
+ var reloadAttempts = [];
9424
+ var createThrottledReload = (interval = 5e3, maxReloads = 3) => {
9425
+ const circuitBreakerWindow = 1e4;
9426
+ const circuitBreakerThreshold = 5;
9427
+ let last = 0;
9428
+ let queued = false;
9429
+ let reloadCount = 0;
9430
+ let firstReloadTime = 0;
9431
+ const resetWindow = 6e4;
9432
+ const doReload = () => {
9433
+ if (typeof window !== "undefined") {
9434
+ const now2 = Date.now();
9435
+ reloadAttempts.push(now2);
9436
+ const cutoff = now2 - circuitBreakerWindow;
9437
+ const recentAttempts = reloadAttempts.filter((t) => t > cutoff);
9438
+ reloadAttempts.length = 0;
9439
+ reloadAttempts.push(...recentAttempts);
9440
+ if (recentAttempts.length >= circuitBreakerThreshold) {
9441
+ console.error(`[Dashboard Reload] Circuit breaker triggered! ${recentAttempts.length} reload attempts in ${circuitBreakerWindow}ms`);
9442
+ const errorDiv = document.createElement("div");
9443
+ errorDiv.id = "reload-circuit-breaker";
9444
+ errorDiv.style.cssText = `
9445
+ position: fixed;
9446
+ top: 20px;
9447
+ left: 50%;
9448
+ transform: translateX(-50%);
9449
+ background: #dc2626;
9450
+ color: white;
9451
+ padding: 20px 32px;
9452
+ border-radius: 12px;
9453
+ z-index: 99999;
9454
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
9455
+ font-family: system-ui, -apple-system, sans-serif;
9456
+ `;
9457
+ errorDiv.innerHTML = `
9458
+ <div style="display: flex; align-items: center; gap: 16px;">
9459
+ <svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
9460
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
9461
+ </svg>
9462
+ <div>
9463
+ <div style="font-weight: 600; font-size: 16px; margin-bottom: 4px;">Too many reload attempts detected</div>
9464
+ <div style="font-size: 14px; opacity: 0.9;">Please check your network connection and refresh manually.</div>
9465
+ </div>
9466
+ </div>
9467
+ `;
9468
+ const existing = document.getElementById("reload-circuit-breaker");
9469
+ if (existing) existing.remove();
9470
+ document.body.appendChild(errorDiv);
9471
+ return;
9472
+ }
9473
+ if (now2 - firstReloadTime > resetWindow) {
9474
+ reloadCount = 0;
9475
+ firstReloadTime = now2;
9476
+ }
9477
+ if (reloadCount === 0) {
9478
+ firstReloadTime = now2;
9479
+ }
9480
+ reloadCount++;
9481
+ if (reloadCount > maxReloads) {
9482
+ console.error(`[Dashboard Reload] Maximum reload attempts (${maxReloads}) reached. Stopping to prevent infinite loop.`);
9483
+ const errorDiv = document.createElement("div");
9484
+ errorDiv.style.cssText = `
9485
+ position: fixed;
9486
+ top: 20px;
9487
+ left: 50%;
9488
+ transform: translateX(-50%);
9489
+ background: #ef4444;
9490
+ color: white;
9491
+ padding: 16px 24px;
9492
+ border-radius: 8px;
9493
+ z-index: 9999;
9494
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
9495
+ `;
9496
+ errorDiv.innerHTML = `
9497
+ <div style="display: flex; align-items: center; gap: 12px;">
9498
+ <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
9499
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
9500
+ </svg>
9501
+ <span>Stream connection failed. Please refresh the page manually.</span>
9502
+ </div>
9503
+ `;
9504
+ document.body.appendChild(errorDiv);
9505
+ setTimeout(() => {
9506
+ document.body.removeChild(errorDiv);
9507
+ }, 1e4);
9508
+ return;
9509
+ }
9510
+ console.warn(`[Dashboard Reload] Reloading dashboard (attempt ${reloadCount}/${maxReloads})`);
9511
+ window.location.reload();
9512
+ }
9513
+ };
9514
+ return () => {
9515
+ const now2 = Date.now();
9516
+ if (now2 - last >= interval) {
9517
+ last = now2;
9518
+ doReload();
9519
+ } else if (!queued) {
9520
+ queued = true;
9521
+ setTimeout(() => {
9522
+ queued = false;
9523
+ last = Date.now();
9524
+ doReload();
9525
+ }, interval - (now2 - last));
9526
+ }
9527
+ };
9528
+ };
9529
+ var throttledReloadDashboard = createThrottledReload(5e3, 3);
9530
+
9386
9531
  // src/lib/utils/index.ts
9387
9532
  var formatIdleTime = (idleTimeInSeconds) => {
9388
9533
  if (!idleTimeInSeconds || idleTimeInSeconds <= 0) {
@@ -18669,6 +18814,7 @@ var VideoGridView = React19__namespace.default.memo(({
18669
18814
  const [gridRows, setGridRows] = React19.useState(1);
18670
18815
  const [visibleWorkspaces, setVisibleWorkspaces] = React19.useState(/* @__PURE__ */ new Set());
18671
18816
  const [cardDimensions, setCardDimensions] = React19.useState({ width: 0, height: 0 });
18817
+ const [failedStreams, setFailedStreams] = React19.useState(/* @__PURE__ */ new Set());
18672
18818
  const videoConfig = useVideoConfig();
18673
18819
  const { cropping, canvasConfig, hlsUrls } = videoConfig;
18674
18820
  const mergedVideoSources = {
@@ -18820,9 +18966,17 @@ var VideoGridView = React19__namespace.default.memo(({
18820
18966
  action_count: workspace.action_count
18821
18967
  });
18822
18968
  const displayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
18823
- const navParams = getWorkspaceNavigationParams(workspaceId, displayName);
18969
+ const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id);
18824
18970
  router$1.push(`/workspace/${workspaceId}${navParams}`);
18825
18971
  }, [router$1]);
18972
+ const handleStreamError = React19.useCallback((workspaceId) => {
18973
+ console.error(`[VideoGridView] Stream failed for workspace: ${workspaceId}`);
18974
+ setFailedStreams((prev) => new Set(prev).add(workspaceId));
18975
+ trackCoreEvent("Video Stream Error", {
18976
+ workspace_id: workspaceId,
18977
+ view_type: "video_grid"
18978
+ });
18979
+ }, []);
18826
18980
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `relative overflow-hidden h-full w-full ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "h-full w-full p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
18827
18981
  "div",
18828
18982
  {
@@ -18859,9 +19013,9 @@ var VideoGridView = React19__namespace.default.memo(({
18859
19013
  {
18860
19014
  workspace,
18861
19015
  hlsUrl: getWorkspaceHlsUrl(workspace.workspace_name, workspace.line_id),
18862
- shouldPlay: isVisible,
19016
+ shouldPlay: isVisible && !failedStreams.has(workspaceId),
18863
19017
  onClick: () => handleWorkspaceClick(workspace),
18864
- onFatalError: throttledReloadDashboard,
19018
+ onFatalError: () => handleStreamError(workspaceId),
18865
19019
  isVeryLowEfficiency,
18866
19020
  cropping: workspaceCropping,
18867
19021
  canvasFps: canvasConfig?.fps,
@@ -24028,7 +24182,7 @@ var WorkspaceGridItem = React19__namespace.default.memo(({
24028
24182
  e.preventDefault();
24029
24183
  if (isInactive) return;
24030
24184
  const displayName = getWorkspaceDisplayName(data.workspace_name, data.line_id);
24031
- const navParams = getWorkspaceNavigationParams(data.workspace_id, displayName);
24185
+ const navParams = getWorkspaceNavigationParams(data.workspace_id, displayName, data.line_id);
24032
24186
  navigate(`/workspace/${data.workspace_id}${navParams}`, {
24033
24187
  trackingEvent: {
24034
24188
  name: "Workspace Detail Clicked",
@@ -28769,6 +28923,73 @@ function HomeView({
28769
28923
  }
28770
28924
  var AuthenticatedHomeView = withAuth(React19__namespace.default.memo(HomeView));
28771
28925
  var HomeView_default = HomeView;
28926
+ function withWorkspaceDisplayNames(Component3, options = {}) {
28927
+ const {
28928
+ showLoading = true,
28929
+ loadingMessage = "Initializing workspace data...",
28930
+ initializeFor = "all"
28931
+ } = options;
28932
+ return function WithWorkspaceDisplayNamesWrapper(props) {
28933
+ const [isInitialized2, setIsInitialized] = React19.useState(false);
28934
+ const [error, setError] = React19.useState(null);
28935
+ React19.useEffect(() => {
28936
+ setIsInitialized(false);
28937
+ setError(null);
28938
+ const initializeDisplayNames = async () => {
28939
+ try {
28940
+ const { lineIds, selectedLineId, factoryViewId } = props;
28941
+ let lineIdArray = [];
28942
+ if (Array.isArray(lineIds)) {
28943
+ lineIdArray = lineIds;
28944
+ } else if (lineIds && typeof lineIds === "object") {
28945
+ lineIdArray = Object.values(lineIds).filter((id3) => !!id3);
28946
+ }
28947
+ if (initializeFor === "specific" && selectedLineId) {
28948
+ if (selectedLineId === factoryViewId && lineIdArray.length > 0) {
28949
+ await Promise.all(
28950
+ lineIdArray.map((lineId) => preInitializeWorkspaceDisplayNames(lineId))
28951
+ );
28952
+ } else {
28953
+ await preInitializeWorkspaceDisplayNames(selectedLineId);
28954
+ }
28955
+ } else if (lineIdArray.length > 0) {
28956
+ await Promise.all(
28957
+ lineIdArray.map((lineId) => preInitializeWorkspaceDisplayNames(lineId))
28958
+ );
28959
+ } else {
28960
+ await preInitializeWorkspaceDisplayNames();
28961
+ }
28962
+ setIsInitialized(true);
28963
+ } catch (err) {
28964
+ console.error("Failed to initialize workspace display names:", err);
28965
+ setError(err);
28966
+ setIsInitialized(true);
28967
+ }
28968
+ };
28969
+ initializeDisplayNames();
28970
+ }, [
28971
+ Array.isArray(props.lineIds) ? props.lineIds.join(",") : JSON.stringify(props.lineIds),
28972
+ props.selectedLineId,
28973
+ props.factoryViewId,
28974
+ initializeFor
28975
+ ]);
28976
+ if (!isInitialized2 && showLoading) {
28977
+ return /* @__PURE__ */ jsxRuntime.jsx(LoadingPage, { message: loadingMessage });
28978
+ }
28979
+ if (error && showLoading) {
28980
+ console.warn("Workspace display names initialization failed, proceeding anyway:", error);
28981
+ }
28982
+ return /* @__PURE__ */ jsxRuntime.jsx(Component3, { ...props });
28983
+ };
28984
+ }
28985
+ var withAllWorkspaceDisplayNames = (Component3) => withWorkspaceDisplayNames(Component3, {
28986
+ initializeFor: "all",
28987
+ showLoading: true
28988
+ });
28989
+ var withSelectedLineDisplayNames = (Component3) => withWorkspaceDisplayNames(Component3, {
28990
+ initializeFor: "specific",
28991
+ showLoading: true
28992
+ });
28772
28993
 
28773
28994
  // src/views/kpi-detail-view.types.ts
28774
28995
  var pageVariants = {
@@ -28829,11 +29050,6 @@ var itemVariants = {
28829
29050
  }
28830
29051
  }
28831
29052
  };
28832
-
28833
- // src/lib/utils/navigation.ts
28834
- function getWorkspaceNavigationParams2(workspaceUuid, displayName) {
28835
- return `?name=${encodeURIComponent(displayName || "")}`;
28836
- }
28837
29053
  var formatLocalDate = (date) => {
28838
29054
  const options = {
28839
29055
  year: "numeric",
@@ -28949,7 +29165,7 @@ var BottomSection = React19.memo(({
28949
29165
  }
28950
29166
  const clickHandler = () => handleWorkspaceClick(ws, index);
28951
29167
  const displayName = getWorkspaceDisplayName(ws.workspace_name, lineId);
28952
- const navParams = wsUuid ? getWorkspaceNavigationParams2(wsUuid, displayName) : "";
29168
+ const navParams = wsUuid ? getWorkspaceNavigationParams(wsUuid, displayName, lineId) : "";
28953
29169
  const dateShiftParams = urlDate ? `&date=${urlDate}&shift=${urlShift || "0"}` : "";
28954
29170
  const returnToParam = `&returnTo=${encodeURIComponent(`/kpis/${lineInfo?.line_id}`)}`;
28955
29171
  const fullUrl = `/workspace/${wsUuid}${navParams}${dateShiftParams}${returnToParam}`;
@@ -28983,7 +29199,7 @@ var BottomSection = React19.memo(({
28983
29199
  sortedByEfficiency.map((ws, index) => {
28984
29200
  const clickHandler = () => handleWorkspaceClick(ws, index);
28985
29201
  const displayName = getWorkspaceDisplayName(ws.workspace_name, lineId);
28986
- const navParams = ws.workspace_uuid ? getWorkspaceNavigationParams2(ws.workspace_uuid, displayName) : "";
29202
+ const navParams = ws.workspace_uuid ? getWorkspaceNavigationParams(ws.workspace_uuid, displayName, lineId) : "";
28987
29203
  const dateShiftParams = urlDate ? `&date=${urlDate}&shift=${urlShift || "0"}` : "";
28988
29204
  const returnToParam = `&returnTo=${encodeURIComponent(`/kpis/${lineInfo?.line_id}`)}`;
28989
29205
  const fullUrl = `/workspace/${ws.workspace_uuid}${navParams}${dateShiftParams}${returnToParam}`;
@@ -29598,7 +29814,8 @@ var KPIDetailView = ({
29598
29814
  ) }) })
29599
29815
  ] });
29600
29816
  };
29601
- var KPIDetailView_default = KPIDetailView;
29817
+ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
29818
+ var KPIDetailView_default = KPIDetailViewWithDisplayNames;
29602
29819
  var LineCard = ({ line, onClick }) => {
29603
29820
  const { kpis, isLoading, error } = useLineKPIs({ lineId: line.id });
29604
29821
  const isOnTrack = React19__namespace.default.useMemo(() => {
@@ -30088,7 +30305,7 @@ var LeaderboardDetailView = React19.memo(({
30088
30305
  action_threshold: workspace.action_threshold
30089
30306
  });
30090
30307
  const displayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
30091
- const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName) : "";
30308
+ const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName, workspace.line_id) : "";
30092
30309
  const returnToParam = `&returnTo=${encodeURIComponent(`/leaderboard`)}`;
30093
30310
  if (onWorkspaceClick) {
30094
30311
  onWorkspaceClick(workspace, rank);
@@ -30222,7 +30439,8 @@ var LeaderboardDetailView = React19.memo(({
30222
30439
  return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && JSON.stringify(prevProps.lineNames) === JSON.stringify(nextProps.lineNames) && prevProps.className === nextProps.className && prevProps.onBackClick === nextProps.onBackClick && prevProps.onWorkspaceClick === nextProps.onWorkspaceClick;
30223
30440
  });
30224
30441
  LeaderboardDetailView.displayName = "LeaderboardDetailView";
30225
- var LeaderboardDetailView_default = LeaderboardDetailView;
30442
+ var LeaderboardDetailViewWithDisplayNames = withAllWorkspaceDisplayNames(LeaderboardDetailView);
30443
+ var LeaderboardDetailView_default = LeaderboardDetailViewWithDisplayNames;
30226
30444
  function LoginView({
30227
30445
  logoSrc = "/optifye-logo.png",
30228
30446
  logoAlt = "Optifye",
@@ -32342,7 +32560,12 @@ var TargetsView = ({
32342
32560
  }
32343
32561
  }), {});
32344
32562
  }, [lineIds]);
32345
- const [lineWorkspaces, setLineWorkspaces] = React19.useState(initialLineWorkspaces);
32563
+ const [allShiftsData, setAllShiftsData] = React19.useState({
32564
+ 0: initialLineWorkspaces,
32565
+ // Day shift
32566
+ 1: initialLineWorkspaces
32567
+ // Night shift (will be populated on first load)
32568
+ });
32346
32569
  const [actionIds, setActionIds] = React19.useState(null);
32347
32570
  const [savingLines, setSavingLines] = React19.useState(
32348
32571
  () => lineIds.reduce((acc, id3) => ({ ...acc, [id3]: false }), {})
@@ -32354,6 +32577,13 @@ var TargetsView = ({
32354
32577
  const [isBulkConfigureOpen, setIsBulkConfigureOpen] = React19.useState(false);
32355
32578
  const [selectedWorkspaces, setSelectedWorkspaces] = React19.useState([]);
32356
32579
  const [selectedShift, setSelectedShift] = React19.useState(0);
32580
+ const lineWorkspaces = allShiftsData[selectedShift] || initialLineWorkspaces;
32581
+ const setLineWorkspaces = React19.useCallback((updater) => {
32582
+ setAllShiftsData((prev) => ({
32583
+ ...prev,
32584
+ [selectedShift]: typeof updater === "function" ? updater(prev[selectedShift]) : updater
32585
+ }));
32586
+ }, [selectedShift]);
32357
32587
  const supabase = useSupabase();
32358
32588
  const auth = useAuth();
32359
32589
  userId || auth?.user?.id;
@@ -32424,7 +32654,7 @@ var TargetsView = ({
32424
32654
  };
32425
32655
  fetchActions();
32426
32656
  }, [supabase, companyId]);
32427
- const fetchLineThresholds = React19.useCallback(async () => {
32657
+ React19.useCallback(async (shiftId) => {
32428
32658
  try {
32429
32659
  if (!supabase) return;
32430
32660
  const currentDate = getOperationalDate();
@@ -32492,15 +32722,7 @@ var TargetsView = ({
32492
32722
  shiftEndTime: endTime,
32493
32723
  breaks,
32494
32724
  shiftHours: Number(shiftHours),
32495
- workspaces: (currentLineStateFromLoop?.workspaces || []).map((ws) => ({
32496
- ...ws,
32497
- targetPPH: ws.targetCycleTime !== "" ? calculatePPH(ws.targetCycleTime, breaks, Number(shiftHours)) : ws.targetPPH,
32498
- targetDayOutput: calculateDayOutput(
32499
- ws.targetCycleTime !== "" ? calculatePPH(ws.targetCycleTime, breaks, Number(shiftHours)) : ws.targetPPH,
32500
- Number(shiftHours),
32501
- breaks
32502
- )
32503
- }))
32725
+ workspaces: currentLineStateFromLoop?.workspaces || []
32504
32726
  };
32505
32727
  hasUpdates = true;
32506
32728
  }
@@ -32514,7 +32736,71 @@ var TargetsView = ({
32514
32736
  } catch (error) {
32515
32737
  console.error("Error in fetchLineThresholds outer try-catch:", error);
32516
32738
  }
32517
- }, [selectedShift, supabase, lineIds, lineWorkspaces]);
32739
+ }, [selectedShift, supabase, lineIds, lineWorkspaces, allShiftsData]);
32740
+ const fetchAllShiftsData = React19.useCallback(async (currentWorkspaces) => {
32741
+ if (!supabase) return;
32742
+ const currentDate = getOperationalDate();
32743
+ const newAllShiftsData = {
32744
+ 0: JSON.parse(JSON.stringify(currentWorkspaces)),
32745
+ // Deep clone for day shift
32746
+ 1: JSON.parse(JSON.stringify(currentWorkspaces))
32747
+ // Deep clone for night shift
32748
+ };
32749
+ for (const shiftId of [0, 1]) {
32750
+ for (const lineId of lineIds) {
32751
+ try {
32752
+ const { data: operatingHours, error: hoursError } = await supabase.from("line_operating_hours").select("start_time, end_time, breaks").eq("line_id", lineId).eq("shift_id", shiftId).maybeSingle();
32753
+ if (hoursError) {
32754
+ console.error(`Error fetching operating hours for line ${lineId}, shift ${shiftId}:`, hoursError);
32755
+ continue;
32756
+ }
32757
+ let breaks = [];
32758
+ if (operatingHours?.breaks) {
32759
+ if (Array.isArray(operatingHours.breaks)) {
32760
+ breaks = operatingHours.breaks.map((breakItem) => ({
32761
+ startTime: breakItem.start || breakItem.startTime || "00:00",
32762
+ endTime: breakItem.end || breakItem.endTime || "00:00",
32763
+ duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
32764
+ }));
32765
+ }
32766
+ }
32767
+ const startTime = operatingHours?.start_time || "08:00";
32768
+ const endTime = operatingHours?.end_time || "19:00";
32769
+ const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
32770
+ const actionThresholds = await workspaceService.getActionThresholds(
32771
+ lineId,
32772
+ currentDate,
32773
+ shiftId
32774
+ );
32775
+ const existingLine = newAllShiftsData[shiftId][lineId];
32776
+ if (existingLine) {
32777
+ newAllShiftsData[shiftId][lineId] = {
32778
+ ...existingLine,
32779
+ shiftStartTime: startTime,
32780
+ shiftEndTime: endTime,
32781
+ breaks,
32782
+ shiftHours: Number(shiftHours),
32783
+ workspaces: existingLine.workspaces.map((ws) => {
32784
+ const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
32785
+ if (threshold) {
32786
+ return {
32787
+ ...ws,
32788
+ targetPPH: threshold.pph_threshold,
32789
+ targetCycleTime: threshold.ideal_cycle_time,
32790
+ targetDayOutput: threshold.total_day_output
32791
+ };
32792
+ }
32793
+ return ws;
32794
+ })
32795
+ };
32796
+ }
32797
+ } catch (error) {
32798
+ console.error(`Error fetching data for line ${lineId}, shift ${shiftId}:`, error);
32799
+ }
32800
+ }
32801
+ }
32802
+ setAllShiftsData(newAllShiftsData);
32803
+ }, [supabase, lineIds]);
32518
32804
  const loadOperatingHours = React19.useCallback(async (lineId, shiftId) => {
32519
32805
  try {
32520
32806
  if (!supabase) return null;
@@ -32619,7 +32905,7 @@ var TargetsView = ({
32619
32905
  (line) => line.workspaces && line.workspaces.length > 0
32620
32906
  );
32621
32907
  if (workspacesAlreadyLoadedForAnyLine) {
32622
- fetchLineThresholds();
32908
+ setIsLoading(false);
32623
32909
  return;
32624
32910
  }
32625
32911
  const fetchWorkspacesAndThenThresholds = async () => {
@@ -32640,7 +32926,7 @@ var TargetsView = ({
32640
32926
  }
32641
32927
  try {
32642
32928
  const fetchedLineWorkspacesData = await workspaceService.getWorkspaces(lineId);
32643
- const mappedWorkspaces = fetchedLineWorkspacesData.map((ws) => ({
32929
+ let mappedWorkspaces = fetchedLineWorkspacesData.map((ws) => ({
32644
32930
  id: ws.id,
32645
32931
  name: ws.workspace_id,
32646
32932
  targetPPH: ws.action_pph_threshold === null ? "" : ws.action_pph_threshold,
@@ -32649,6 +32935,29 @@ var TargetsView = ({
32649
32935
  actionType: ws.action_id === actionIds.assembly ? "assembly" : ws.action_id === actionIds.packaging ? "packaging" : "assembly",
32650
32936
  actionId: ws.action_id === actionIds.assembly ? actionIds.assembly : ws.action_id === actionIds.packaging ? actionIds.packaging : actionIds.assembly
32651
32937
  })).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
32938
+ const currentDate = getOperationalDate();
32939
+ try {
32940
+ const actionThresholds = await workspaceService.getActionThresholds(
32941
+ lineId,
32942
+ currentDate,
32943
+ selectedShift
32944
+ );
32945
+ mappedWorkspaces = mappedWorkspaces.map((ws) => {
32946
+ const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
32947
+ if (threshold) {
32948
+ return {
32949
+ ...ws,
32950
+ targetPPH: threshold.pph_threshold,
32951
+ targetCycleTime: threshold.ideal_cycle_time,
32952
+ targetDayOutput: threshold.total_day_output
32953
+ };
32954
+ }
32955
+ return ws;
32956
+ });
32957
+ console.log(`Merged action thresholds for line ${lineId}, found ${actionThresholds.length} saved thresholds`);
32958
+ } catch (error) {
32959
+ console.error(`Error fetching action thresholds for line ${lineId}:`, error);
32960
+ }
32652
32961
  if (JSON.stringify(mappedWorkspaces) !== JSON.stringify(newUpdatedLineWorkspaces[lineId].workspaces)) {
32653
32962
  newUpdatedLineWorkspaces[lineId] = {
32654
32963
  ...newUpdatedLineWorkspaces[lineId],
@@ -32663,7 +32972,7 @@ var TargetsView = ({
32663
32972
  if (workspacesHaveBeenUpdated) {
32664
32973
  setLineWorkspaces(newUpdatedLineWorkspaces);
32665
32974
  }
32666
- await fetchLineThresholds();
32975
+ await fetchAllShiftsData(newUpdatedLineWorkspaces);
32667
32976
  } catch (error) {
32668
32977
  console.error("Error in fetchWorkspacesAndThenThresholds:", error);
32669
32978
  sonner.toast.error("Failed to load workspace data");
@@ -32672,37 +32981,7 @@ var TargetsView = ({
32672
32981
  }
32673
32982
  };
32674
32983
  fetchWorkspacesAndThenThresholds();
32675
- }, [actionIds, supabase, lineIds, lineWorkspaces, fetchLineThresholds]);
32676
- React19.useEffect(() => {
32677
- if (Object.keys(lineWorkspaces).length > 0) {
32678
- const updatedLineWorkspaces = { ...lineWorkspaces };
32679
- let hasChanges = false;
32680
- Object.keys(updatedLineWorkspaces).forEach((lineId) => {
32681
- const line = updatedLineWorkspaces[lineId];
32682
- const shiftHours = calculateShiftHours2(line.shiftStartTime, line.shiftEndTime, line.breaks);
32683
- if (shiftHours !== line.shiftHours) {
32684
- hasChanges = true;
32685
- updatedLineWorkspaces[lineId] = {
32686
- ...line,
32687
- shiftHours,
32688
- workspaces: line.workspaces.map((ws) => {
32689
- const targetDayOutput = calculateDayOutput(ws.targetPPH, shiftHours, line.breaks);
32690
- if (targetDayOutput !== ws.targetDayOutput) {
32691
- return {
32692
- ...ws,
32693
- targetDayOutput
32694
- };
32695
- }
32696
- return ws;
32697
- })
32698
- };
32699
- }
32700
- });
32701
- if (hasChanges) {
32702
- setLineWorkspaces(updatedLineWorkspaces);
32703
- }
32704
- }
32705
- }, [selectedShift]);
32984
+ }, [actionIds, supabase, lineIds]);
32706
32985
  const toggleLineDropdown = React19.useCallback((lineId) => {
32707
32986
  setLineWorkspaces((prev) => {
32708
32987
  const newIsOpen = !prev[lineId].isOpen;
@@ -32786,6 +33065,16 @@ var TargetsView = ({
32786
33065
  updates.targetDayOutput = calculateDayOutput(value, shiftHours, prev[lineId].breaks);
32787
33066
  } else if (field === "targetDayOutput") {
32788
33067
  updates.targetDayOutput = value;
33068
+ if (value !== "") {
33069
+ const breaks = prev[lineId].breaks;
33070
+ const totalBreakMinutes = breaks.reduce((total, b) => total + b.duration, 0);
33071
+ const totalBreakHours = totalBreakMinutes / 60;
33072
+ const realWorkHours = shiftHours - totalBreakHours;
33073
+ const calculatedPPH = Math.round(value / realWorkHours);
33074
+ updates.targetPPH = calculatedPPH;
33075
+ } else {
33076
+ updates.targetPPH = "";
33077
+ }
32789
33078
  }
32790
33079
  updates[field] = value;
32791
33080
  return updates;
@@ -33047,8 +33336,9 @@ var TargetsView = ({
33047
33336
  }
33048
33337
  );
33049
33338
  };
33050
- var TargetsView_default = TargetsView;
33051
- var AuthenticatedTargetsView = withAuth(TargetsView);
33339
+ var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
33340
+ var TargetsView_default = TargetsViewWithDisplayNames;
33341
+ var AuthenticatedTargetsView = withAuth(TargetsViewWithDisplayNames);
33052
33342
 
33053
33343
  // src/views/workspace-detail-view.utils.ts
33054
33344
  var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
@@ -33064,8 +33354,8 @@ var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
33064
33354
  });
33065
33355
  return formatter.format(date);
33066
33356
  };
33067
- var formatWorkspaceName3 = (name) => {
33068
- return getWorkspaceDisplayName(name);
33357
+ var formatWorkspaceName3 = (name, lineId) => {
33358
+ return getWorkspaceDisplayName(name, lineId);
33069
33359
  };
33070
33360
  var getDaysDifference = (date) => {
33071
33361
  const today = /* @__PURE__ */ new Date();
@@ -33102,9 +33392,11 @@ var WorkspaceDetailView = ({
33102
33392
  fromMonthly = false,
33103
33393
  sourceType,
33104
33394
  displayName,
33395
+ lineId,
33105
33396
  defaultTab,
33106
33397
  returnUrl,
33107
33398
  lineIds = { line1: "", line2: "" },
33399
+ selectedLineId,
33108
33400
  onNavigate,
33109
33401
  onTabChange,
33110
33402
  onDateSelect,
@@ -33267,7 +33559,8 @@ var WorkspaceDetailView = ({
33267
33559
  setSelectedMonth(newMonth);
33268
33560
  setSelectedYear(newYear);
33269
33561
  };
33270
- const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "");
33562
+ const effectiveLineId = lineId || selectedLineId;
33563
+ const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
33271
33564
  const shouldShowCycleTimeChart = showCycleTimeChart ?? formattedWorkspaceName.startsWith("FINAL ASSY");
33272
33565
  const handleBackNavigation = () => {
33273
33566
  if (returnUrl) {
@@ -33279,13 +33572,23 @@ var WorkspaceDetailView = ({
33279
33572
  if (date || shift) {
33280
33573
  setActiveTab("monthly_history");
33281
33574
  if (onNavigate) {
33282
- onNavigate(`/workspace/${workspaceId}?fromMonthly=true`);
33575
+ const params = new URLSearchParams();
33576
+ params.set("fromMonthly", "true");
33577
+ if (effectiveLineId) {
33578
+ params.set("lineId", effectiveLineId);
33579
+ }
33580
+ onNavigate(`/workspace/${workspaceId}?${params.toString()}`);
33283
33581
  }
33284
33582
  } else {
33285
33583
  if (previousView === "line_monthly_history") {
33286
33584
  setActiveTab("monthly_history");
33287
33585
  if (onNavigate) {
33288
- onNavigate(`/workspace/${workspaceId}?fromMonthly=true`);
33586
+ const params = new URLSearchParams();
33587
+ params.set("fromMonthly", "true");
33588
+ if (effectiveLineId) {
33589
+ params.set("lineId", effectiveLineId);
33590
+ }
33591
+ onNavigate(`/workspace/${workspaceId}?${params.toString()}`);
33289
33592
  }
33290
33593
  } else {
33291
33594
  if (onNavigate) {
@@ -33743,7 +34046,13 @@ var WorkspaceDetailView = ({
33743
34046
  if (onDateSelect) {
33744
34047
  onDateSelect(selectedDate, selectedShift);
33745
34048
  } else if (onNavigate) {
33746
- onNavigate(`/workspace/${workspaceId}?date=${selectedDate}&shift=${selectedShift === "day" ? "0" : "1"}`);
34049
+ const params = new URLSearchParams();
34050
+ params.set("date", selectedDate);
34051
+ params.set("shift", selectedShift === "day" ? "0" : "1");
34052
+ if (effectiveLineId) {
34053
+ params.set("lineId", effectiveLineId);
34054
+ }
34055
+ onNavigate(`/workspace/${workspaceId}?${params.toString()}`);
33747
34056
  }
33748
34057
  },
33749
34058
  onMonthNavigate: (newMonth, newYear) => {
@@ -33770,7 +34079,12 @@ var WorkspaceDetailView = ({
33770
34079
  }
33771
34080
  );
33772
34081
  };
33773
- var WrappedComponent = withAuth(WorkspaceDetailView);
34082
+ var WorkspaceDetailViewWithDisplayNames = withSelectedLineDisplayNames(WorkspaceDetailView);
34083
+ var WorkspaceDetailViewFinal = (props) => {
34084
+ const componentKey = `${props.date || "live"}_${props.shift || "current"}`;
34085
+ return /* @__PURE__ */ jsxRuntime.jsx(WorkspaceDetailViewWithDisplayNames, { ...props }, componentKey);
34086
+ };
34087
+ var WrappedComponent = withAuth(WorkspaceDetailViewFinal);
33774
34088
  var WorkspaceDetailView_default = WrappedComponent;
33775
34089
  var SKUManagementView = () => {
33776
34090
  const config = useDashboardConfig();
@@ -34344,6 +34658,7 @@ exports.identifyCoreUser = identifyCoreUser;
34344
34658
  exports.initializeCoreMixpanel = initializeCoreMixpanel;
34345
34659
  exports.isLegacyConfiguration = isLegacyConfiguration;
34346
34660
  exports.isTransitionPeriod = isTransitionPeriod;
34661
+ exports.isUrlPermanentlyFailed = isUrlPermanentlyFailed;
34347
34662
  exports.isValidFactoryViewConfiguration = isValidFactoryViewConfiguration;
34348
34663
  exports.isValidLineInfoPayload = isValidLineInfoPayload;
34349
34664
  exports.isValidWorkspaceDetailedMetricsPayload = isValidWorkspaceDetailedMetricsPayload;
@@ -34363,6 +34678,7 @@ exports.qualityService = qualityService;
34363
34678
  exports.realtimeService = realtimeService;
34364
34679
  exports.refreshWorkspaceDisplayNames = refreshWorkspaceDisplayNames;
34365
34680
  exports.resetCoreMixpanel = resetCoreMixpanel;
34681
+ exports.resetFailedUrl = resetFailedUrl;
34366
34682
  exports.resetSubscriptionManager = resetSubscriptionManager;
34367
34683
  exports.s3VideoPreloader = s3VideoPreloader;
34368
34684
  exports.skuService = skuService;