@optifye/dashboard-core 6.0.9 → 6.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -12,7 +12,7 @@ import { noop, warning, invariant, progress, secondsToMilliseconds, milliseconds
12
12
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
13
13
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
14
14
  import { Slot } from '@radix-ui/react-slot';
15
- import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, Minus, ArrowDown, ArrowUp, Search, CheckCircle, AlertTriangle, Info, Share2, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2, ArrowLeft, RefreshCw, Menu, Send, Copy, Edit2, UserCheck, Save, LogOut, Calendar, Package, Settings, LifeBuoy, ArrowLeftIcon as ArrowLeftIcon$1, Settings2, CheckCircle2, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
15
+ import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, X, Coffee, Plus, Clock, Minus, ArrowDown, ArrowUp, Search, CheckCircle, AlertTriangle, Info, Share2, Download, User, XCircle, ChevronLeft, ChevronRight, AlertCircle, Sun, Moon, MessageSquare, Trash2, ArrowLeft, RefreshCw, Menu, Send, Copy, Edit2, UserCheck, Save, LogOut, Calendar, Package, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle, ArrowLeftIcon as ArrowLeftIcon$1, Settings2, CheckCircle2 } from 'lucide-react';
16
16
  import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
17
17
  import html2canvas from 'html2canvas';
18
18
  import jsPDF, { jsPDF as jsPDF$1 } from 'jspdf';
@@ -4987,34 +4987,6 @@ var useWorkspaceOperators = (workspaceId, options) => {
4987
4987
  refetch: fetchData
4988
4988
  };
4989
4989
  };
4990
-
4991
- // src/lib/utils/dashboardReload.ts
4992
- var createThrottledReload = (interval = 5e3) => {
4993
- let last = 0;
4994
- let queued = false;
4995
- const doReload = () => {
4996
- if (typeof window !== "undefined") {
4997
- window.location.reload();
4998
- }
4999
- };
5000
- return () => {
5001
- const now2 = Date.now();
5002
- if (now2 - last >= interval) {
5003
- last = now2;
5004
- doReload();
5005
- } else if (!queued) {
5006
- queued = true;
5007
- setTimeout(() => {
5008
- queued = false;
5009
- last = Date.now();
5010
- doReload();
5011
- }, interval - (now2 - last));
5012
- }
5013
- };
5014
- };
5015
- var throttledReloadDashboard = createThrottledReload(5e3);
5016
-
5017
- // src/lib/hooks/useHlsStream.ts
5018
4990
  var HLS_CONFIG = {
5019
4991
  maxBufferLength: 8,
5020
4992
  maxMaxBufferLength: 15,
@@ -5033,7 +5005,35 @@ var HLS_CONFIG = {
5033
5005
  liveSyncDurationCount: 2
5034
5006
  // Follow live edge aggressively
5035
5007
  };
5008
+ var failedUrls = /* @__PURE__ */ new Map();
5009
+ var MAX_FAILURES_PER_URL = 3;
5010
+ var FAILURE_EXPIRY_MS = 5 * 60 * 1e3;
5011
+ function resetFailedUrl(url) {
5012
+ if (failedUrls.has(url)) {
5013
+ console.log(`[HLS] Manually resetting failed URL: ${url}`);
5014
+ failedUrls.delete(url);
5015
+ }
5016
+ }
5017
+ function isUrlPermanentlyFailed(url) {
5018
+ const failure = failedUrls.get(url);
5019
+ return failure?.permanentlyFailed || false;
5020
+ }
5036
5021
  function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5022
+ const urlFailure = failedUrls.get(src);
5023
+ const isPermanentlyFailed = urlFailure?.permanentlyFailed || false;
5024
+ const cleanupFailedUrls = () => {
5025
+ const now2 = Date.now();
5026
+ const expiredUrls = [];
5027
+ failedUrls.forEach((failure, url) => {
5028
+ if (now2 - failure.timestamp > FAILURE_EXPIRY_MS) {
5029
+ expiredUrls.push(url);
5030
+ }
5031
+ });
5032
+ expiredUrls.forEach((url) => {
5033
+ console.log(`[HLS] Removing expired failure entry for: ${url}`);
5034
+ failedUrls.delete(url);
5035
+ });
5036
+ };
5037
5037
  const [restartKey, setRestartKey] = useState(0);
5038
5038
  const hlsRef = useRef(null);
5039
5039
  const stallCheckIntervalRef = useRef(null);
@@ -5103,14 +5103,33 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5103
5103
  };
5104
5104
  const hardRestart = (reason) => {
5105
5105
  console.warn(`[HLS] Hard restart: ${reason}`);
5106
+ cleanupFailedUrls();
5107
+ const failure = failedUrls.get(src);
5108
+ const failureCount = failure ? failure.count : 0;
5109
+ if (failureCount >= MAX_FAILURES_PER_URL) {
5110
+ console.error(`[HLS] URL has failed ${failureCount} times. Marking as permanently failed: ${src}`);
5111
+ failedUrls.set(src, {
5112
+ count: failureCount,
5113
+ timestamp: Date.now(),
5114
+ permanentlyFailed: true
5115
+ });
5116
+ cleanup();
5117
+ if (onFatalError) {
5118
+ onFatalError();
5119
+ }
5120
+ return;
5121
+ }
5122
+ failedUrls.set(src, {
5123
+ count: failureCount + 1,
5124
+ timestamp: Date.now(),
5125
+ permanentlyFailed: false
5126
+ });
5106
5127
  cleanup();
5107
5128
  setRestartKey((k) => k + 1);
5108
5129
  softRestartCountRef.current = 0;
5109
- if (reason.includes("hard restart") || reason.includes("native video error")) {
5130
+ if (reason.includes("404 hard restart")) {
5110
5131
  if (onFatalError) {
5111
5132
  onFatalError();
5112
- } else {
5113
- throttledReloadDashboard();
5114
5133
  }
5115
5134
  }
5116
5135
  };
@@ -5164,6 +5183,13 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5164
5183
  }, 7e3);
5165
5184
  };
5166
5185
  useEffect(() => {
5186
+ if (isPermanentlyFailed) {
5187
+ console.warn(`[HLS] URL is permanently failed, not attempting to load: ${src}`);
5188
+ if (onFatalError) {
5189
+ onFatalError();
5190
+ }
5191
+ return;
5192
+ }
5167
5193
  if (!src || !shouldPlay) {
5168
5194
  cleanup();
5169
5195
  return;
@@ -5194,6 +5220,10 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5194
5220
  }
5195
5221
  });
5196
5222
  hls.on(Hls2.Events.MANIFEST_PARSED, () => {
5223
+ if (failedUrls.has(src)) {
5224
+ console.log(`[HLS] Stream loaded successfully, resetting failure count for: ${src}`);
5225
+ failedUrls.delete(src);
5226
+ }
5197
5227
  video.play().catch((err) => {
5198
5228
  console.error("[HLS] Play failed:", err);
5199
5229
  });
@@ -5212,7 +5242,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
5212
5242
  console.error("[HLS] HLS not supported");
5213
5243
  }
5214
5244
  return cleanup;
5215
- }, [src, shouldPlay, restartKey, onFatalError]);
5245
+ }, [src, shouldPlay, restartKey, onFatalError, isPermanentlyFailed]);
5216
5246
  return {
5217
5247
  restartKey,
5218
5248
  isNativeHls: isNativeHlsRef.current
@@ -6317,7 +6347,7 @@ function useNavigation(customNavigate) {
6317
6347
  }
6318
6348
  function useWorkspaceNavigation() {
6319
6349
  const { defaultTimezone } = useDateTimeConfig();
6320
- const getWorkspaceNavigationParams3 = useCallback(
6350
+ const getWorkspaceNavigationParams2 = useCallback(
6321
6351
  (workspaceId, options) => {
6322
6352
  let dateToUse = options?.date;
6323
6353
  if (!dateToUse && options?.useCurrentDate) {
@@ -6333,7 +6363,7 @@ function useWorkspaceNavigation() {
6333
6363
  [defaultTimezone]
6334
6364
  );
6335
6365
  return {
6336
- getWorkspaceNavigationParams: getWorkspaceNavigationParams3
6366
+ getWorkspaceNavigationParams: getWorkspaceNavigationParams2
6337
6367
  };
6338
6368
  }
6339
6369
  function useDateFormatter() {
@@ -9031,9 +9061,15 @@ var getStoredWorkspaceMappings = () => {
9031
9061
  var getDefaultTabForWorkspace = (workspaceId, displayName) => {
9032
9062
  return "overview";
9033
9063
  };
9034
- var getWorkspaceNavigationParams = (workspaceId, displayName) => {
9064
+ var getWorkspaceNavigationParams = (workspaceId, displayName, lineId) => {
9035
9065
  const defaultTab = getDefaultTabForWorkspace();
9036
- return `?displayName=${encodeURIComponent(displayName)}&defaultTab=${defaultTab}`;
9066
+ const params = new URLSearchParams();
9067
+ params.set("displayName", displayName);
9068
+ params.set("defaultTab", defaultTab);
9069
+ if (lineId) {
9070
+ params.set("lineId", lineId);
9071
+ }
9072
+ return `?${params.toString()}`;
9037
9073
  };
9038
9074
 
9039
9075
  // src/lib/utils/videoPreloader.ts
@@ -9354,6 +9390,115 @@ var clearS3VideoCache = () => {
9354
9390
  var preloadS3VideoUrl = s3VideoPreloader.preloadVideo;
9355
9391
  var preloadS3VideosUrl = s3VideoPreloader.preloadVideos;
9356
9392
 
9393
+ // src/lib/utils/dashboardReload.ts
9394
+ var reloadAttempts = [];
9395
+ var createThrottledReload = (interval = 5e3, maxReloads = 3) => {
9396
+ const circuitBreakerWindow = 1e4;
9397
+ const circuitBreakerThreshold = 5;
9398
+ let last = 0;
9399
+ let queued = false;
9400
+ let reloadCount = 0;
9401
+ let firstReloadTime = 0;
9402
+ const resetWindow = 6e4;
9403
+ const doReload = () => {
9404
+ if (typeof window !== "undefined") {
9405
+ const now2 = Date.now();
9406
+ reloadAttempts.push(now2);
9407
+ const cutoff = now2 - circuitBreakerWindow;
9408
+ const recentAttempts = reloadAttempts.filter((t) => t > cutoff);
9409
+ reloadAttempts.length = 0;
9410
+ reloadAttempts.push(...recentAttempts);
9411
+ if (recentAttempts.length >= circuitBreakerThreshold) {
9412
+ console.error(`[Dashboard Reload] Circuit breaker triggered! ${recentAttempts.length} reload attempts in ${circuitBreakerWindow}ms`);
9413
+ const errorDiv = document.createElement("div");
9414
+ errorDiv.id = "reload-circuit-breaker";
9415
+ errorDiv.style.cssText = `
9416
+ position: fixed;
9417
+ top: 20px;
9418
+ left: 50%;
9419
+ transform: translateX(-50%);
9420
+ background: #dc2626;
9421
+ color: white;
9422
+ padding: 20px 32px;
9423
+ border-radius: 12px;
9424
+ z-index: 99999;
9425
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
9426
+ font-family: system-ui, -apple-system, sans-serif;
9427
+ `;
9428
+ errorDiv.innerHTML = `
9429
+ <div style="display: flex; align-items: center; gap: 16px;">
9430
+ <svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
9431
+ <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>
9432
+ </svg>
9433
+ <div>
9434
+ <div style="font-weight: 600; font-size: 16px; margin-bottom: 4px;">Too many reload attempts detected</div>
9435
+ <div style="font-size: 14px; opacity: 0.9;">Please check your network connection and refresh manually.</div>
9436
+ </div>
9437
+ </div>
9438
+ `;
9439
+ const existing = document.getElementById("reload-circuit-breaker");
9440
+ if (existing) existing.remove();
9441
+ document.body.appendChild(errorDiv);
9442
+ return;
9443
+ }
9444
+ if (now2 - firstReloadTime > resetWindow) {
9445
+ reloadCount = 0;
9446
+ firstReloadTime = now2;
9447
+ }
9448
+ if (reloadCount === 0) {
9449
+ firstReloadTime = now2;
9450
+ }
9451
+ reloadCount++;
9452
+ if (reloadCount > maxReloads) {
9453
+ console.error(`[Dashboard Reload] Maximum reload attempts (${maxReloads}) reached. Stopping to prevent infinite loop.`);
9454
+ const errorDiv = document.createElement("div");
9455
+ errorDiv.style.cssText = `
9456
+ position: fixed;
9457
+ top: 20px;
9458
+ left: 50%;
9459
+ transform: translateX(-50%);
9460
+ background: #ef4444;
9461
+ color: white;
9462
+ padding: 16px 24px;
9463
+ border-radius: 8px;
9464
+ z-index: 9999;
9465
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
9466
+ `;
9467
+ errorDiv.innerHTML = `
9468
+ <div style="display: flex; align-items: center; gap: 12px;">
9469
+ <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
9470
+ <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>
9471
+ </svg>
9472
+ <span>Stream connection failed. Please refresh the page manually.</span>
9473
+ </div>
9474
+ `;
9475
+ document.body.appendChild(errorDiv);
9476
+ setTimeout(() => {
9477
+ document.body.removeChild(errorDiv);
9478
+ }, 1e4);
9479
+ return;
9480
+ }
9481
+ console.warn(`[Dashboard Reload] Reloading dashboard (attempt ${reloadCount}/${maxReloads})`);
9482
+ window.location.reload();
9483
+ }
9484
+ };
9485
+ return () => {
9486
+ const now2 = Date.now();
9487
+ if (now2 - last >= interval) {
9488
+ last = now2;
9489
+ doReload();
9490
+ } else if (!queued) {
9491
+ queued = true;
9492
+ setTimeout(() => {
9493
+ queued = false;
9494
+ last = Date.now();
9495
+ doReload();
9496
+ }, interval - (now2 - last));
9497
+ }
9498
+ };
9499
+ };
9500
+ var throttledReloadDashboard = createThrottledReload(5e3, 3);
9501
+
9357
9502
  // src/lib/utils/index.ts
9358
9503
  var formatIdleTime = (idleTimeInSeconds) => {
9359
9504
  if (!idleTimeInSeconds || idleTimeInSeconds <= 0) {
@@ -18640,6 +18785,7 @@ var VideoGridView = React19__default.memo(({
18640
18785
  const [gridRows, setGridRows] = useState(1);
18641
18786
  const [visibleWorkspaces, setVisibleWorkspaces] = useState(/* @__PURE__ */ new Set());
18642
18787
  const [cardDimensions, setCardDimensions] = useState({ width: 0, height: 0 });
18788
+ const [failedStreams, setFailedStreams] = useState(/* @__PURE__ */ new Set());
18643
18789
  const videoConfig = useVideoConfig();
18644
18790
  const { cropping, canvasConfig, hlsUrls } = videoConfig;
18645
18791
  const mergedVideoSources = {
@@ -18791,9 +18937,17 @@ var VideoGridView = React19__default.memo(({
18791
18937
  action_count: workspace.action_count
18792
18938
  });
18793
18939
  const displayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
18794
- const navParams = getWorkspaceNavigationParams(workspaceId, displayName);
18940
+ const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id);
18795
18941
  router.push(`/workspace/${workspaceId}${navParams}`);
18796
18942
  }, [router]);
18943
+ const handleStreamError = useCallback((workspaceId) => {
18944
+ console.error(`[VideoGridView] Stream failed for workspace: ${workspaceId}`);
18945
+ setFailedStreams((prev) => new Set(prev).add(workspaceId));
18946
+ trackCoreEvent("Video Stream Error", {
18947
+ workspace_id: workspaceId,
18948
+ view_type: "video_grid"
18949
+ });
18950
+ }, []);
18797
18951
  return /* @__PURE__ */ jsx("div", { className: `relative overflow-hidden h-full w-full ${className}`, children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "h-full w-full p-2", children: /* @__PURE__ */ jsx(
18798
18952
  "div",
18799
18953
  {
@@ -18830,9 +18984,9 @@ var VideoGridView = React19__default.memo(({
18830
18984
  {
18831
18985
  workspace,
18832
18986
  hlsUrl: getWorkspaceHlsUrl(workspace.workspace_name, workspace.line_id),
18833
- shouldPlay: isVisible,
18987
+ shouldPlay: isVisible && !failedStreams.has(workspaceId),
18834
18988
  onClick: () => handleWorkspaceClick(workspace),
18835
- onFatalError: throttledReloadDashboard,
18989
+ onFatalError: () => handleStreamError(workspaceId),
18836
18990
  isVeryLowEfficiency,
18837
18991
  cropping: workspaceCropping,
18838
18992
  canvasFps: canvasConfig?.fps,
@@ -23999,7 +24153,7 @@ var WorkspaceGridItem = React19__default.memo(({
23999
24153
  e.preventDefault();
24000
24154
  if (isInactive) return;
24001
24155
  const displayName = getWorkspaceDisplayName(data.workspace_name, data.line_id);
24002
- const navParams = getWorkspaceNavigationParams(data.workspace_id, displayName);
24156
+ const navParams = getWorkspaceNavigationParams(data.workspace_id, displayName, data.line_id);
24003
24157
  navigate(`/workspace/${data.workspace_id}${navParams}`, {
24004
24158
  trackingEvent: {
24005
24159
  name: "Workspace Detail Clicked",
@@ -28740,6 +28894,73 @@ function HomeView({
28740
28894
  }
28741
28895
  var AuthenticatedHomeView = withAuth(React19__default.memo(HomeView));
28742
28896
  var HomeView_default = HomeView;
28897
+ function withWorkspaceDisplayNames(Component3, options = {}) {
28898
+ const {
28899
+ showLoading = true,
28900
+ loadingMessage = "Initializing workspace data...",
28901
+ initializeFor = "all"
28902
+ } = options;
28903
+ return function WithWorkspaceDisplayNamesWrapper(props) {
28904
+ const [isInitialized2, setIsInitialized] = useState(false);
28905
+ const [error, setError] = useState(null);
28906
+ useEffect(() => {
28907
+ setIsInitialized(false);
28908
+ setError(null);
28909
+ const initializeDisplayNames = async () => {
28910
+ try {
28911
+ const { lineIds, selectedLineId, factoryViewId } = props;
28912
+ let lineIdArray = [];
28913
+ if (Array.isArray(lineIds)) {
28914
+ lineIdArray = lineIds;
28915
+ } else if (lineIds && typeof lineIds === "object") {
28916
+ lineIdArray = Object.values(lineIds).filter((id3) => !!id3);
28917
+ }
28918
+ if (initializeFor === "specific" && selectedLineId) {
28919
+ if (selectedLineId === factoryViewId && lineIdArray.length > 0) {
28920
+ await Promise.all(
28921
+ lineIdArray.map((lineId) => preInitializeWorkspaceDisplayNames(lineId))
28922
+ );
28923
+ } else {
28924
+ await preInitializeWorkspaceDisplayNames(selectedLineId);
28925
+ }
28926
+ } else if (lineIdArray.length > 0) {
28927
+ await Promise.all(
28928
+ lineIdArray.map((lineId) => preInitializeWorkspaceDisplayNames(lineId))
28929
+ );
28930
+ } else {
28931
+ await preInitializeWorkspaceDisplayNames();
28932
+ }
28933
+ setIsInitialized(true);
28934
+ } catch (err) {
28935
+ console.error("Failed to initialize workspace display names:", err);
28936
+ setError(err);
28937
+ setIsInitialized(true);
28938
+ }
28939
+ };
28940
+ initializeDisplayNames();
28941
+ }, [
28942
+ Array.isArray(props.lineIds) ? props.lineIds.join(",") : JSON.stringify(props.lineIds),
28943
+ props.selectedLineId,
28944
+ props.factoryViewId,
28945
+ initializeFor
28946
+ ]);
28947
+ if (!isInitialized2 && showLoading) {
28948
+ return /* @__PURE__ */ jsx(LoadingPage, { message: loadingMessage });
28949
+ }
28950
+ if (error && showLoading) {
28951
+ console.warn("Workspace display names initialization failed, proceeding anyway:", error);
28952
+ }
28953
+ return /* @__PURE__ */ jsx(Component3, { ...props });
28954
+ };
28955
+ }
28956
+ var withAllWorkspaceDisplayNames = (Component3) => withWorkspaceDisplayNames(Component3, {
28957
+ initializeFor: "all",
28958
+ showLoading: true
28959
+ });
28960
+ var withSelectedLineDisplayNames = (Component3) => withWorkspaceDisplayNames(Component3, {
28961
+ initializeFor: "specific",
28962
+ showLoading: true
28963
+ });
28743
28964
 
28744
28965
  // src/views/kpi-detail-view.types.ts
28745
28966
  var pageVariants = {
@@ -28800,11 +29021,6 @@ var itemVariants = {
28800
29021
  }
28801
29022
  }
28802
29023
  };
28803
-
28804
- // src/lib/utils/navigation.ts
28805
- function getWorkspaceNavigationParams2(workspaceUuid, displayName) {
28806
- return `?name=${encodeURIComponent(displayName || "")}`;
28807
- }
28808
29024
  var formatLocalDate = (date) => {
28809
29025
  const options = {
28810
29026
  year: "numeric",
@@ -28920,7 +29136,7 @@ var BottomSection = memo(({
28920
29136
  }
28921
29137
  const clickHandler = () => handleWorkspaceClick(ws, index);
28922
29138
  const displayName = getWorkspaceDisplayName(ws.workspace_name, lineId);
28923
- const navParams = wsUuid ? getWorkspaceNavigationParams2(wsUuid, displayName) : "";
29139
+ const navParams = wsUuid ? getWorkspaceNavigationParams(wsUuid, displayName, lineId) : "";
28924
29140
  const dateShiftParams = urlDate ? `&date=${urlDate}&shift=${urlShift || "0"}` : "";
28925
29141
  const returnToParam = `&returnTo=${encodeURIComponent(`/kpis/${lineInfo?.line_id}`)}`;
28926
29142
  const fullUrl = `/workspace/${wsUuid}${navParams}${dateShiftParams}${returnToParam}`;
@@ -28954,7 +29170,7 @@ var BottomSection = memo(({
28954
29170
  sortedByEfficiency.map((ws, index) => {
28955
29171
  const clickHandler = () => handleWorkspaceClick(ws, index);
28956
29172
  const displayName = getWorkspaceDisplayName(ws.workspace_name, lineId);
28957
- const navParams = ws.workspace_uuid ? getWorkspaceNavigationParams2(ws.workspace_uuid, displayName) : "";
29173
+ const navParams = ws.workspace_uuid ? getWorkspaceNavigationParams(ws.workspace_uuid, displayName, lineId) : "";
28958
29174
  const dateShiftParams = urlDate ? `&date=${urlDate}&shift=${urlShift || "0"}` : "";
28959
29175
  const returnToParam = `&returnTo=${encodeURIComponent(`/kpis/${lineInfo?.line_id}`)}`;
28960
29176
  const fullUrl = `/workspace/${ws.workspace_uuid}${navParams}${dateShiftParams}${returnToParam}`;
@@ -29569,7 +29785,8 @@ var KPIDetailView = ({
29569
29785
  ) }) })
29570
29786
  ] });
29571
29787
  };
29572
- var KPIDetailView_default = KPIDetailView;
29788
+ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
29789
+ var KPIDetailView_default = KPIDetailViewWithDisplayNames;
29573
29790
  var LineCard = ({ line, onClick }) => {
29574
29791
  const { kpis, isLoading, error } = useLineKPIs({ lineId: line.id });
29575
29792
  const isOnTrack = React19__default.useMemo(() => {
@@ -30059,7 +30276,7 @@ var LeaderboardDetailView = memo(({
30059
30276
  action_threshold: workspace.action_threshold
30060
30277
  });
30061
30278
  const displayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
30062
- const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName) : "";
30279
+ const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName, workspace.line_id) : "";
30063
30280
  const returnToParam = `&returnTo=${encodeURIComponent(`/leaderboard`)}`;
30064
30281
  if (onWorkspaceClick) {
30065
30282
  onWorkspaceClick(workspace, rank);
@@ -30193,7 +30410,8 @@ var LeaderboardDetailView = memo(({
30193
30410
  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;
30194
30411
  });
30195
30412
  LeaderboardDetailView.displayName = "LeaderboardDetailView";
30196
- var LeaderboardDetailView_default = LeaderboardDetailView;
30413
+ var LeaderboardDetailViewWithDisplayNames = withAllWorkspaceDisplayNames(LeaderboardDetailView);
30414
+ var LeaderboardDetailView_default = LeaderboardDetailViewWithDisplayNames;
30197
30415
  function LoginView({
30198
30416
  logoSrc = "/optifye-logo.png",
30199
30417
  logoAlt = "Optifye",
@@ -32313,7 +32531,12 @@ var TargetsView = ({
32313
32531
  }
32314
32532
  }), {});
32315
32533
  }, [lineIds]);
32316
- const [lineWorkspaces, setLineWorkspaces] = useState(initialLineWorkspaces);
32534
+ const [allShiftsData, setAllShiftsData] = useState({
32535
+ 0: initialLineWorkspaces,
32536
+ // Day shift
32537
+ 1: initialLineWorkspaces
32538
+ // Night shift (will be populated on first load)
32539
+ });
32317
32540
  const [actionIds, setActionIds] = useState(null);
32318
32541
  const [savingLines, setSavingLines] = useState(
32319
32542
  () => lineIds.reduce((acc, id3) => ({ ...acc, [id3]: false }), {})
@@ -32325,6 +32548,13 @@ var TargetsView = ({
32325
32548
  const [isBulkConfigureOpen, setIsBulkConfigureOpen] = useState(false);
32326
32549
  const [selectedWorkspaces, setSelectedWorkspaces] = useState([]);
32327
32550
  const [selectedShift, setSelectedShift] = useState(0);
32551
+ const lineWorkspaces = allShiftsData[selectedShift] || initialLineWorkspaces;
32552
+ const setLineWorkspaces = useCallback((updater) => {
32553
+ setAllShiftsData((prev) => ({
32554
+ ...prev,
32555
+ [selectedShift]: typeof updater === "function" ? updater(prev[selectedShift]) : updater
32556
+ }));
32557
+ }, [selectedShift]);
32328
32558
  const supabase = useSupabase();
32329
32559
  const auth = useAuth();
32330
32560
  userId || auth?.user?.id;
@@ -32332,70 +32562,120 @@ var TargetsView = ({
32332
32562
  const { skus, isLoading: skusLoading } = useSKUs(companyId);
32333
32563
  const skuEnabled = dashboardConfig?.skuConfig?.enabled || false;
32334
32564
  useEffect(() => {
32335
- const fetchLineDetails = async () => {
32565
+ let timeoutId;
32566
+ let retryCount = 0;
32567
+ const MAX_RETRIES2 = 2;
32568
+ const LOADING_TIMEOUT = 15e3;
32569
+ const fetchInitialData = async () => {
32336
32570
  if (!supabase || lineIds.length === 0) return;
32337
- const detailsPromises = lineIds.map(async (lineId) => {
32338
- try {
32339
- const { data, error } = await supabase.from("lines").select("factory_id").eq("id", lineId).single();
32340
- if (error) {
32341
- console.error(`Error fetching factory_id for line ${lineId}:`, error);
32342
- return { lineId, factoryId: void 0 };
32343
- }
32344
- return { lineId, factoryId: data?.factory_id };
32345
- } catch (err) {
32346
- console.error(`Exception fetching factory_id for line ${lineId}:`, err);
32347
- return { lineId, factoryId: void 0 };
32571
+ setIsLoading(true);
32572
+ timeoutId = setTimeout(() => {
32573
+ console.error("Loading timeout reached");
32574
+ if (retryCount < MAX_RETRIES2) {
32575
+ retryCount++;
32576
+ console.log(`Retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
32577
+ toast.warning("Loading is taking longer than expected. Retrying...");
32578
+ fetchInitialData();
32579
+ } else {
32580
+ setIsLoading(false);
32581
+ toast.error("Failed to load data. Please refresh the page.");
32348
32582
  }
32349
- });
32350
- const results = await Promise.all(detailsPromises);
32351
- setLineWorkspaces((prev) => {
32352
- const newWorkspaces = { ...prev };
32353
- let needsUpdate = false;
32354
- results.forEach((result) => {
32355
- if (result.factoryId && newWorkspaces[result.lineId] && newWorkspaces[result.lineId].factoryId !== result.factoryId) {
32356
- newWorkspaces[result.lineId].factoryId = result.factoryId;
32357
- needsUpdate = true;
32358
- }
32359
- });
32360
- return needsUpdate ? newWorkspaces : prev;
32361
- });
32362
- };
32363
- fetchLineDetails();
32364
- }, [supabase, lineIds]);
32365
- useEffect(() => {
32366
- const fetchActions = async () => {
32367
- if (!supabase) {
32368
- console.error("Supabase client not available in fetchActions");
32369
- toast.error("Initialization error, please refresh.");
32370
- return;
32371
- }
32583
+ }, LOADING_TIMEOUT);
32372
32584
  try {
32373
- const actions = await actionService.getActionsByName(
32374
- [ACTION_NAMES.ASSEMBLY, ACTION_NAMES.PACKAGING],
32375
- companyId
32376
- );
32585
+ const [factoryResults, actions] = await Promise.all([
32586
+ // Fetch factory IDs
32587
+ Promise.all(lineIds.map(async (lineId) => {
32588
+ try {
32589
+ const { data, error } = await supabase.from("lines").select("factory_id").eq("id", lineId).single();
32590
+ if (error) {
32591
+ console.error(`Error fetching factory_id for line ${lineId}:`, error);
32592
+ return { lineId, factoryId: void 0 };
32593
+ }
32594
+ return { lineId, factoryId: data?.factory_id };
32595
+ } catch (err) {
32596
+ console.error(`Exception fetching factory_id for line ${lineId}:`, err);
32597
+ return { lineId, factoryId: void 0 };
32598
+ }
32599
+ })),
32600
+ // Fetch action IDs
32601
+ actionService.getActionsByName(
32602
+ [ACTION_NAMES.ASSEMBLY, ACTION_NAMES.PACKAGING],
32603
+ companyId
32604
+ )
32605
+ ]);
32377
32606
  const assemblyAction = actions.find((a) => a.action_name === ACTION_NAMES.ASSEMBLY);
32378
32607
  const packagingAction = actions.find((a) => a.action_name === ACTION_NAMES.PACKAGING);
32379
32608
  if (!assemblyAction || !packagingAction) {
32380
32609
  throw new Error("Could not find required actions");
32381
32610
  }
32382
- const ids = {
32611
+ const actionIdsData = {
32383
32612
  assembly: assemblyAction.id,
32384
32613
  packaging: packagingAction.id
32385
32614
  };
32386
- console.log("Fetched Action IDs:", {
32387
- "Assembly Action ID": ids.assembly,
32388
- "Packaging Action ID": ids.packaging
32615
+ setActionIds(actionIdsData);
32616
+ const updatedLineWorkspaces = { ...initialLineWorkspaces };
32617
+ factoryResults.forEach((result) => {
32618
+ if (result.factoryId && updatedLineWorkspaces[result.lineId]) {
32619
+ updatedLineWorkspaces[result.lineId].factoryId = result.factoryId;
32620
+ }
32389
32621
  });
32390
- setActionIds(ids);
32622
+ const currentDate = getOperationalDate();
32623
+ for (const lineId of lineIds) {
32624
+ if (!updatedLineWorkspaces[lineId]?.factoryId) {
32625
+ console.warn(`Skipping workspace fetch for line ${lineId} - no factory ID`);
32626
+ continue;
32627
+ }
32628
+ try {
32629
+ const workspacesData = await workspaceService.getWorkspaces(lineId);
32630
+ const actionThresholds = await workspaceService.getActionThresholds(
32631
+ lineId,
32632
+ currentDate,
32633
+ selectedShift
32634
+ );
32635
+ const mappedWorkspaces = workspacesData.map((ws) => {
32636
+ const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
32637
+ return {
32638
+ id: ws.id,
32639
+ name: ws.workspace_id,
32640
+ targetPPH: threshold?.pph_threshold ?? (ws.action_pph_threshold === null ? "" : ws.action_pph_threshold),
32641
+ targetCycleTime: threshold?.ideal_cycle_time ?? (ws.action_cycle_time === null ? "" : ws.action_cycle_time),
32642
+ targetDayOutput: threshold?.total_day_output ?? (ws.action_total_day_output === null ? "" : ws.action_total_day_output),
32643
+ actionType: ws.action_id === actionIdsData.assembly ? "assembly" : ws.action_id === actionIdsData.packaging ? "packaging" : "assembly",
32644
+ actionId: ws.action_id === actionIdsData.assembly ? actionIdsData.assembly : ws.action_id === actionIdsData.packaging ? actionIdsData.packaging : actionIdsData.assembly
32645
+ };
32646
+ }).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
32647
+ updatedLineWorkspaces[lineId].workspaces = mappedWorkspaces;
32648
+ } catch (error) {
32649
+ console.error(`Error fetching workspace data for line ${lineId}:`, error);
32650
+ }
32651
+ }
32652
+ setLineWorkspaces(updatedLineWorkspaces);
32653
+ await fetchAllShiftsData(updatedLineWorkspaces);
32391
32654
  } catch (error) {
32392
- console.error("Error fetching actions:", error);
32393
- toast.error("Failed to load action data");
32655
+ console.error("Error fetching initial data:", error);
32656
+ clearTimeout(timeoutId);
32657
+ if (retryCount < MAX_RETRIES2) {
32658
+ retryCount++;
32659
+ console.log(`Error occurred, retrying... (attempt ${retryCount + 1}/${MAX_RETRIES2 + 1})`);
32660
+ toast.warning("Error loading data. Retrying...");
32661
+ setTimeout(() => fetchInitialData(), 1e3);
32662
+ } else {
32663
+ toast.error("Failed to load initial data");
32664
+ setIsLoading(false);
32665
+ }
32666
+ } finally {
32667
+ clearTimeout(timeoutId);
32668
+ if (retryCount === 0 || retryCount >= MAX_RETRIES2) {
32669
+ setIsLoading(false);
32670
+ }
32394
32671
  }
32395
32672
  };
32396
- fetchActions();
32397
- }, [supabase, companyId]);
32398
- const fetchLineThresholds = useCallback(async () => {
32673
+ fetchInitialData();
32674
+ return () => {
32675
+ clearTimeout(timeoutId);
32676
+ };
32677
+ }, [supabase, lineIds, companyId]);
32678
+ useCallback(async (shiftId) => {
32399
32679
  try {
32400
32680
  if (!supabase) return;
32401
32681
  const currentDate = getOperationalDate();
@@ -32463,15 +32743,7 @@ var TargetsView = ({
32463
32743
  shiftEndTime: endTime,
32464
32744
  breaks,
32465
32745
  shiftHours: Number(shiftHours),
32466
- workspaces: (currentLineStateFromLoop?.workspaces || []).map((ws) => ({
32467
- ...ws,
32468
- targetPPH: ws.targetCycleTime !== "" ? calculatePPH(ws.targetCycleTime, breaks, Number(shiftHours)) : ws.targetPPH,
32469
- targetDayOutput: calculateDayOutput(
32470
- ws.targetCycleTime !== "" ? calculatePPH(ws.targetCycleTime, breaks, Number(shiftHours)) : ws.targetPPH,
32471
- Number(shiftHours),
32472
- breaks
32473
- )
32474
- }))
32746
+ workspaces: currentLineStateFromLoop?.workspaces || []
32475
32747
  };
32476
32748
  hasUpdates = true;
32477
32749
  }
@@ -32485,7 +32757,71 @@ var TargetsView = ({
32485
32757
  } catch (error) {
32486
32758
  console.error("Error in fetchLineThresholds outer try-catch:", error);
32487
32759
  }
32488
- }, [selectedShift, supabase, lineIds, lineWorkspaces]);
32760
+ }, [selectedShift, supabase, lineIds, lineWorkspaces, allShiftsData]);
32761
+ const fetchAllShiftsData = useCallback(async (currentWorkspaces) => {
32762
+ if (!supabase) return;
32763
+ const currentDate = getOperationalDate();
32764
+ const newAllShiftsData = {
32765
+ 0: JSON.parse(JSON.stringify(currentWorkspaces)),
32766
+ // Deep clone for day shift
32767
+ 1: JSON.parse(JSON.stringify(currentWorkspaces))
32768
+ // Deep clone for night shift
32769
+ };
32770
+ for (const shiftId of [0, 1]) {
32771
+ for (const lineId of lineIds) {
32772
+ try {
32773
+ 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();
32774
+ if (hoursError) {
32775
+ console.error(`Error fetching operating hours for line ${lineId}, shift ${shiftId}:`, hoursError);
32776
+ continue;
32777
+ }
32778
+ let breaks = [];
32779
+ if (operatingHours?.breaks) {
32780
+ if (Array.isArray(operatingHours.breaks)) {
32781
+ breaks = operatingHours.breaks.map((breakItem) => ({
32782
+ startTime: breakItem.start || breakItem.startTime || "00:00",
32783
+ endTime: breakItem.end || breakItem.endTime || "00:00",
32784
+ duration: breakItem.duration || calculateShiftHours2(breakItem.start || breakItem.startTime || "00:00", breakItem.end || breakItem.endTime || "00:00", []) * 60
32785
+ }));
32786
+ }
32787
+ }
32788
+ const startTime = operatingHours?.start_time || "08:00";
32789
+ const endTime = operatingHours?.end_time || "19:00";
32790
+ const shiftHours = calculateShiftHours2(startTime, endTime, breaks);
32791
+ const actionThresholds = await workspaceService.getActionThresholds(
32792
+ lineId,
32793
+ currentDate,
32794
+ shiftId
32795
+ );
32796
+ const existingLine = newAllShiftsData[shiftId][lineId];
32797
+ if (existingLine) {
32798
+ newAllShiftsData[shiftId][lineId] = {
32799
+ ...existingLine,
32800
+ shiftStartTime: startTime,
32801
+ shiftEndTime: endTime,
32802
+ breaks,
32803
+ shiftHours: Number(shiftHours),
32804
+ workspaces: existingLine.workspaces.map((ws) => {
32805
+ const threshold = actionThresholds.find((t) => t.workspace_id === ws.id);
32806
+ if (threshold) {
32807
+ return {
32808
+ ...ws,
32809
+ targetPPH: threshold.pph_threshold,
32810
+ targetCycleTime: threshold.ideal_cycle_time,
32811
+ targetDayOutput: threshold.total_day_output
32812
+ };
32813
+ }
32814
+ return ws;
32815
+ })
32816
+ };
32817
+ }
32818
+ } catch (error) {
32819
+ console.error(`Error fetching data for line ${lineId}, shift ${shiftId}:`, error);
32820
+ }
32821
+ }
32822
+ }
32823
+ setAllShiftsData(newAllShiftsData);
32824
+ }, [supabase, lineIds]);
32489
32825
  const loadOperatingHours = useCallback(async (lineId, shiftId) => {
32490
32826
  try {
32491
32827
  if (!supabase) return null;
@@ -32581,99 +32917,6 @@ var TargetsView = ({
32581
32917
  };
32582
32918
  }
32583
32919
  }, [supabase]);
32584
- useEffect(() => {
32585
- const allFactoryIdsLoaded = lineIds.every((lineId) => lineWorkspaces[lineId]?.factoryId);
32586
- if (!actionIds || !allFactoryIdsLoaded) {
32587
- return;
32588
- }
32589
- const workspacesAlreadyLoadedForAnyLine = Object.values(lineWorkspaces).some(
32590
- (line) => line.workspaces && line.workspaces.length > 0
32591
- );
32592
- if (workspacesAlreadyLoadedForAnyLine) {
32593
- fetchLineThresholds();
32594
- return;
32595
- }
32596
- const fetchWorkspacesAndThenThresholds = async () => {
32597
- setIsLoading(true);
32598
- try {
32599
- if (!supabase) {
32600
- toast.error("Supabase client not available");
32601
- setIsLoading(false);
32602
- return;
32603
- }
32604
- console.log("Starting workspace fetch with actionIds and loaded factoryIds.");
32605
- const newUpdatedLineWorkspaces = { ...lineWorkspaces };
32606
- let workspacesHaveBeenUpdated = false;
32607
- for (const lineId of lineIds) {
32608
- if (!newUpdatedLineWorkspaces[lineId]?.factoryId) {
32609
- console.warn(`FactoryId for line ${lineId} missing during workspace fetch. Skipping its workspaces.`);
32610
- continue;
32611
- }
32612
- try {
32613
- const fetchedLineWorkspacesData = await workspaceService.getWorkspaces(lineId);
32614
- const mappedWorkspaces = fetchedLineWorkspacesData.map((ws) => ({
32615
- id: ws.id,
32616
- name: ws.workspace_id,
32617
- targetPPH: ws.action_pph_threshold === null ? "" : ws.action_pph_threshold,
32618
- targetCycleTime: ws.action_cycle_time === null ? "" : ws.action_cycle_time,
32619
- targetDayOutput: ws.action_total_day_output === null ? "" : ws.action_total_day_output,
32620
- actionType: ws.action_id === actionIds.assembly ? "assembly" : ws.action_id === actionIds.packaging ? "packaging" : "assembly",
32621
- actionId: ws.action_id === actionIds.assembly ? actionIds.assembly : ws.action_id === actionIds.packaging ? actionIds.packaging : actionIds.assembly
32622
- })).sort((a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }));
32623
- if (JSON.stringify(mappedWorkspaces) !== JSON.stringify(newUpdatedLineWorkspaces[lineId].workspaces)) {
32624
- newUpdatedLineWorkspaces[lineId] = {
32625
- ...newUpdatedLineWorkspaces[lineId],
32626
- workspaces: mappedWorkspaces
32627
- };
32628
- workspacesHaveBeenUpdated = true;
32629
- }
32630
- } catch (error) {
32631
- console.error(`Error fetching workspaces for line ${lineId}:`, error);
32632
- }
32633
- }
32634
- if (workspacesHaveBeenUpdated) {
32635
- setLineWorkspaces(newUpdatedLineWorkspaces);
32636
- }
32637
- await fetchLineThresholds();
32638
- } catch (error) {
32639
- console.error("Error in fetchWorkspacesAndThenThresholds:", error);
32640
- toast.error("Failed to load workspace data");
32641
- } finally {
32642
- setIsLoading(false);
32643
- }
32644
- };
32645
- fetchWorkspacesAndThenThresholds();
32646
- }, [actionIds, supabase, lineIds, lineWorkspaces, fetchLineThresholds]);
32647
- useEffect(() => {
32648
- if (Object.keys(lineWorkspaces).length > 0) {
32649
- const updatedLineWorkspaces = { ...lineWorkspaces };
32650
- let hasChanges = false;
32651
- Object.keys(updatedLineWorkspaces).forEach((lineId) => {
32652
- const line = updatedLineWorkspaces[lineId];
32653
- const shiftHours = calculateShiftHours2(line.shiftStartTime, line.shiftEndTime, line.breaks);
32654
- if (shiftHours !== line.shiftHours) {
32655
- hasChanges = true;
32656
- updatedLineWorkspaces[lineId] = {
32657
- ...line,
32658
- shiftHours,
32659
- workspaces: line.workspaces.map((ws) => {
32660
- const targetDayOutput = calculateDayOutput(ws.targetPPH, shiftHours, line.breaks);
32661
- if (targetDayOutput !== ws.targetDayOutput) {
32662
- return {
32663
- ...ws,
32664
- targetDayOutput
32665
- };
32666
- }
32667
- return ws;
32668
- })
32669
- };
32670
- }
32671
- });
32672
- if (hasChanges) {
32673
- setLineWorkspaces(updatedLineWorkspaces);
32674
- }
32675
- }
32676
- }, [selectedShift]);
32677
32920
  const toggleLineDropdown = useCallback((lineId) => {
32678
32921
  setLineWorkspaces((prev) => {
32679
32922
  const newIsOpen = !prev[lineId].isOpen;
@@ -32757,6 +33000,16 @@ var TargetsView = ({
32757
33000
  updates.targetDayOutput = calculateDayOutput(value, shiftHours, prev[lineId].breaks);
32758
33001
  } else if (field === "targetDayOutput") {
32759
33002
  updates.targetDayOutput = value;
33003
+ if (value !== "") {
33004
+ const breaks = prev[lineId].breaks;
33005
+ const totalBreakMinutes = breaks.reduce((total, b) => total + b.duration, 0);
33006
+ const totalBreakHours = totalBreakMinutes / 60;
33007
+ const realWorkHours = shiftHours - totalBreakHours;
33008
+ const calculatedPPH = Math.round(value / realWorkHours);
33009
+ updates.targetPPH = calculatedPPH;
33010
+ } else {
33011
+ updates.targetPPH = "";
33012
+ }
32760
33013
  }
32761
33014
  updates[field] = value;
32762
33015
  return updates;
@@ -32890,14 +33143,15 @@ var TargetsView = ({
32890
33143
  console.log(`[handleSaveLine] workspaceThresholdUpdates for ${lineId}:`, workspaceThresholdUpdates);
32891
33144
  await workspaceService.updateActionThresholds(workspaceThresholdUpdates);
32892
33145
  console.log(`[handleSaveLine] Successfully updated action thresholds for ${lineId}`);
33146
+ const packagingWorkspaces = lineDataToSave.workspaces.filter((ws) => ws.actionType === "packaging");
32893
33147
  const lineThresholdData = {
32894
33148
  factory_id: currentFactoryId,
32895
33149
  line_id: lineId,
32896
33150
  date: currentDate,
32897
33151
  shift_id: selectedShift,
32898
33152
  product_code: lineDataToSave.productId,
32899
- threshold_day_output: lineDataToSave.workspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
32900
- threshold_pph: lineDataToSave.workspaces.reduce((acc, ws) => acc + (Number(ws.targetPPH) || 0), 0),
33153
+ threshold_day_output: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetDayOutput) || 0), 0),
33154
+ threshold_pph: packagingWorkspaces.reduce((acc, ws) => acc + (Number(ws.targetPPH) || 0), 0),
32901
33155
  ...skuEnabled && lineDataToSave.selectedSKU ? { sku_id: lineDataToSave.selectedSKU.id } : {}
32902
33156
  };
32903
33157
  console.log(`[handleSaveLine] lineThresholdData for upsert on ${lineId}:`, lineThresholdData);
@@ -33018,8 +33272,9 @@ var TargetsView = ({
33018
33272
  }
33019
33273
  );
33020
33274
  };
33021
- var TargetsView_default = TargetsView;
33022
- var AuthenticatedTargetsView = withAuth(TargetsView);
33275
+ var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
33276
+ var TargetsView_default = TargetsViewWithDisplayNames;
33277
+ var AuthenticatedTargetsView = withAuth(TargetsViewWithDisplayNames);
33023
33278
 
33024
33279
  // src/views/workspace-detail-view.utils.ts
33025
33280
  var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
@@ -33035,8 +33290,8 @@ var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
33035
33290
  });
33036
33291
  return formatter.format(date);
33037
33292
  };
33038
- var formatWorkspaceName3 = (name) => {
33039
- return getWorkspaceDisplayName(name);
33293
+ var formatWorkspaceName3 = (name, lineId) => {
33294
+ return getWorkspaceDisplayName(name, lineId);
33040
33295
  };
33041
33296
  var getDaysDifference = (date) => {
33042
33297
  const today = /* @__PURE__ */ new Date();
@@ -33073,9 +33328,11 @@ var WorkspaceDetailView = ({
33073
33328
  fromMonthly = false,
33074
33329
  sourceType,
33075
33330
  displayName,
33331
+ lineId,
33076
33332
  defaultTab,
33077
33333
  returnUrl,
33078
33334
  lineIds = { line1: "", line2: "" },
33335
+ selectedLineId,
33079
33336
  onNavigate,
33080
33337
  onTabChange,
33081
33338
  onDateSelect,
@@ -33238,7 +33495,8 @@ var WorkspaceDetailView = ({
33238
33495
  setSelectedMonth(newMonth);
33239
33496
  setSelectedYear(newYear);
33240
33497
  };
33241
- const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "");
33498
+ const effectiveLineId = lineId || selectedLineId;
33499
+ const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
33242
33500
  const shouldShowCycleTimeChart = showCycleTimeChart ?? formattedWorkspaceName.startsWith("FINAL ASSY");
33243
33501
  const handleBackNavigation = () => {
33244
33502
  if (returnUrl) {
@@ -33250,13 +33508,23 @@ var WorkspaceDetailView = ({
33250
33508
  if (date || shift) {
33251
33509
  setActiveTab("monthly_history");
33252
33510
  if (onNavigate) {
33253
- onNavigate(`/workspace/${workspaceId}?fromMonthly=true`);
33511
+ const params = new URLSearchParams();
33512
+ params.set("fromMonthly", "true");
33513
+ if (effectiveLineId) {
33514
+ params.set("lineId", effectiveLineId);
33515
+ }
33516
+ onNavigate(`/workspace/${workspaceId}?${params.toString()}`);
33254
33517
  }
33255
33518
  } else {
33256
33519
  if (previousView === "line_monthly_history") {
33257
33520
  setActiveTab("monthly_history");
33258
33521
  if (onNavigate) {
33259
- onNavigate(`/workspace/${workspaceId}?fromMonthly=true`);
33522
+ const params = new URLSearchParams();
33523
+ params.set("fromMonthly", "true");
33524
+ if (effectiveLineId) {
33525
+ params.set("lineId", effectiveLineId);
33526
+ }
33527
+ onNavigate(`/workspace/${workspaceId}?${params.toString()}`);
33260
33528
  }
33261
33529
  } else {
33262
33530
  if (onNavigate) {
@@ -33714,7 +33982,13 @@ var WorkspaceDetailView = ({
33714
33982
  if (onDateSelect) {
33715
33983
  onDateSelect(selectedDate, selectedShift);
33716
33984
  } else if (onNavigate) {
33717
- onNavigate(`/workspace/${workspaceId}?date=${selectedDate}&shift=${selectedShift === "day" ? "0" : "1"}`);
33985
+ const params = new URLSearchParams();
33986
+ params.set("date", selectedDate);
33987
+ params.set("shift", selectedShift === "day" ? "0" : "1");
33988
+ if (effectiveLineId) {
33989
+ params.set("lineId", effectiveLineId);
33990
+ }
33991
+ onNavigate(`/workspace/${workspaceId}?${params.toString()}`);
33718
33992
  }
33719
33993
  },
33720
33994
  onMonthNavigate: (newMonth, newYear) => {
@@ -33741,7 +34015,12 @@ var WorkspaceDetailView = ({
33741
34015
  }
33742
34016
  );
33743
34017
  };
33744
- var WrappedComponent = withAuth(WorkspaceDetailView);
34018
+ var WorkspaceDetailViewWithDisplayNames = withSelectedLineDisplayNames(WorkspaceDetailView);
34019
+ var WorkspaceDetailViewFinal = (props) => {
34020
+ const componentKey = `${props.date || "live"}_${props.shift || "current"}`;
34021
+ return /* @__PURE__ */ jsx(WorkspaceDetailViewWithDisplayNames, { ...props }, componentKey);
34022
+ };
34023
+ var WrappedComponent = withAuth(WorkspaceDetailViewFinal);
33745
34024
  var WorkspaceDetailView_default = WrappedComponent;
33746
34025
  var SKUManagementView = () => {
33747
34026
  const config = useDashboardConfig();
@@ -34119,4 +34398,4 @@ var S3Service = class {
34119
34398
  }
34120
34399
  };
34121
34400
 
34122
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, ProfileView_default as ProfileView, RegistryProvider, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SlackAPI, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, cacheService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isTransitionPeriod, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetSubscriptionManager, s3VideoPreloader, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };
34401
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedTargetsView, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, EmptyStateMessage, FactoryView_default as FactoryView, GaugeChart, GridComponentsPlaceholder, Header, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, NoWorkspaceData, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, ProfileView_default as ProfileView, RegistryProvider, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SingleVideoStream_default as SingleVideoStream, Skeleton, SlackAPI, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TimeDisplay_default as TimeDisplay, TimePickerDropdown, VideoCard, VideoGridView, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, cacheService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createStreamProxyHandler, createSupabaseClient, createThrottledReload, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAuth, useAuthConfig, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, videoPreloader, whatsappService, withAuth, withRegistry, workspaceService };