@optifye/dashboard-core 6.11.14 → 6.11.16

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
@@ -11771,6 +11771,9 @@ var toWorkspaceDetailedMetrics = ({
11771
11771
  const idealOutput = coerceNumber(data.ideal_output ?? data.ideal_output_until_now, 0);
11772
11772
  const outputDifference = totalActions - idealOutput;
11773
11773
  const hourlyCycleTimes = Array.isArray(data.hourly_cycle_times) ? data.hourly_cycle_times.map((value) => coerceNumber(value, 0)) : [];
11774
+ const cycleCompletionClipCount = data.cycle_completion_clip_count === null || data.cycle_completion_clip_count === void 0 ? null : coerceNumber(data.cycle_completion_clip_count, 0);
11775
+ const cycleTimeDataStatus = data.cycle_time_data_status === "missing_clips" ? "missing_clips" : data.cycle_time_data_status === "available" ? "available" : null;
11776
+ const cycleTimeTimezone = typeof data.cycle_time_timezone === "string" ? data.cycle_time_timezone : null;
11774
11777
  const totalWorkspacesValue = coerceNumber(
11775
11778
  data.total_workspaces ?? lineMetricsById?.[data.line_id || ""]?.total_workspaces ?? workspaceConfig.totalWorkspaces,
11776
11779
  0
@@ -11818,6 +11821,9 @@ var toWorkspaceDetailedMetrics = ({
11818
11821
  total_actions: totalActions,
11819
11822
  hourly_action_counts: hourlyActionCounts,
11820
11823
  hourly_cycle_times: hourlyCycleTimes,
11824
+ cycle_completion_clip_count: cycleCompletionClipCount,
11825
+ cycle_time_data_status: cycleTimeDataStatus,
11826
+ cycle_time_timezone: cycleTimeTimezone,
11821
11827
  workspace_rank: coerceNumber(data.workspace_rank, 0),
11822
11828
  total_workspaces: totalWorkspacesValue,
11823
11829
  ideal_output_until_now: idealOutput,
@@ -12858,6 +12864,7 @@ var useShiftGroups = ({
12858
12864
  }) => {
12859
12865
  const [shiftGroups, setShiftGroups] = React141.useState([]);
12860
12866
  const [shiftGroupsKey, setShiftGroupsKey] = React141.useState("");
12867
+ const [hasComputed, setHasComputed] = React141.useState(false);
12861
12868
  const lastKeyRef = React141.useRef("");
12862
12869
  const mapRef = React141.useRef(shiftConfigMap);
12863
12870
  React141.useEffect(() => {
@@ -12868,6 +12875,7 @@ var useShiftGroups = ({
12868
12875
  lastKeyRef.current = "";
12869
12876
  setShiftGroups([]);
12870
12877
  setShiftGroupsKey("");
12878
+ setHasComputed(false);
12871
12879
  return;
12872
12880
  }
12873
12881
  let isMounted = true;
@@ -12880,6 +12888,7 @@ var useShiftGroups = ({
12880
12888
  setShiftGroups(groups);
12881
12889
  setShiftGroupsKey(key);
12882
12890
  }
12891
+ setHasComputed(true);
12883
12892
  };
12884
12893
  compute();
12885
12894
  const intervalId = setInterval(compute, pollIntervalMs);
@@ -12888,7 +12897,7 @@ var useShiftGroups = ({
12888
12897
  clearInterval(intervalId);
12889
12898
  };
12890
12899
  }, [enabled, shiftConfigMap.size, timezone, pollIntervalMs]);
12891
- return { shiftGroups, shiftGroupsKey };
12900
+ return { shiftGroups, shiftGroupsKey, hasComputed };
12892
12901
  };
12893
12902
 
12894
12903
  // src/lib/types/efficiencyLegend.ts
@@ -12950,52 +12959,6 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
12950
12959
  }
12951
12960
  }
12952
12961
 
12953
- // src/lib/hooks/useDashboardMetrics.recentFlow.ts
12954
- var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
12955
- var getWorkspaceRecentFlowCacheKey = (workspace) => {
12956
- const workspaceKey = workspace.workspace_uuid || workspace.workspace_name || "unknown";
12957
- return `${workspaceKey}|${workspace.date}|${workspace.shift_id}`;
12958
- };
12959
- var toRecentFlowSnapshot = (workspace) => {
12960
- if (!isFiniteNumber(workspace.recent_flow_percent)) {
12961
- return null;
12962
- }
12963
- return {
12964
- recent_flow_percent: workspace.recent_flow_percent,
12965
- recent_flow_actual_rate_pph: workspace.recent_flow_actual_rate_pph ?? null,
12966
- recent_flow_healthy_rate_pph: workspace.recent_flow_healthy_rate_pph ?? null,
12967
- recent_flow_window_minutes: workspace.recent_flow_window_minutes ?? null,
12968
- recent_flow_effective_end_at: workspace.recent_flow_effective_end_at ?? null
12969
- };
12970
- };
12971
- var mergeWorkspaceRecentFlowMetrics = (workspaces, previousCache) => {
12972
- const nextCache = /* @__PURE__ */ new Map();
12973
- const mergedWorkspaces = workspaces.map((workspace) => {
12974
- const cacheKey = getWorkspaceRecentFlowCacheKey(workspace);
12975
- const currentSnapshot = toRecentFlowSnapshot(workspace);
12976
- if (workspace.recent_flow_mode === "computed" && currentSnapshot) {
12977
- nextCache.set(cacheKey, currentSnapshot);
12978
- return workspace;
12979
- }
12980
- if (workspace.recent_flow_mode === "hold") {
12981
- const cachedSnapshot = previousCache.get(cacheKey);
12982
- if (cachedSnapshot) {
12983
- nextCache.set(cacheKey, cachedSnapshot);
12984
- return {
12985
- ...workspace,
12986
- ...cachedSnapshot,
12987
- recent_flow_mode: "hold"
12988
- };
12989
- }
12990
- }
12991
- return workspace;
12992
- });
12993
- return {
12994
- workspaces: mergedWorkspaces,
12995
- cache: nextCache
12996
- };
12997
- };
12998
-
12999
12962
  // src/lib/hooks/useDashboardMetrics.ts
13000
12963
  var DEBUG_DASHBOARD_LOGS = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
13001
12964
  var logDebug = (...args) => {
@@ -13084,7 +13047,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13084
13047
  const abortControllerRef = React141.useRef(null);
13085
13048
  const lastFetchKeyRef = React141.useRef(null);
13086
13049
  const inFlightFetchKeyRef = React141.useRef(null);
13087
- const recentFlowCacheRef = React141.useRef(/* @__PURE__ */ new Map());
13088
13050
  const updateQueueRef = React141.useRef(false);
13089
13051
  const onLineMetricsUpdateRef = React141.useRef(onLineMetricsUpdate);
13090
13052
  const shiftGroupsRef = React141.useRef(shiftGroups);
@@ -13118,7 +13080,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13118
13080
  setError(null);
13119
13081
  lastFetchKeyRef.current = null;
13120
13082
  inFlightFetchKeyRef.current = null;
13121
- recentFlowCacheRef.current = /* @__PURE__ */ new Map();
13122
13083
  }, [lineId]);
13123
13084
  const fetchAllMetrics = React141.useCallback(async (options = {}) => {
13124
13085
  const { force = false } = options;
@@ -13393,12 +13354,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13393
13354
  actionType: item.action_type,
13394
13355
  actionName: item.action_name
13395
13356
  }),
13396
- recent_flow_mode: item.recent_flow_mode ?? void 0,
13397
13357
  recent_flow_percent: item.recent_flow_percent ?? null,
13398
- recent_flow_actual_rate_pph: item.recent_flow_actual_rate_pph ?? null,
13399
- recent_flow_healthy_rate_pph: item.recent_flow_healthy_rate_pph ?? null,
13400
- recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
13401
13358
  recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
13359
+ recent_flow_computed_at: item.recent_flow_computed_at ?? null,
13402
13360
  incoming_wip_current: item.incoming_wip_current ?? null,
13403
13361
  incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
13404
13362
  incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null
@@ -13409,16 +13367,11 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13409
13367
  const wsNumB = parseInt(b.workspace_name?.replace(/[^0-9]/g, "") || "0");
13410
13368
  return wsNumA - wsNumB;
13411
13369
  });
13412
- const {
13413
- workspaces: mergedWorkspaceData,
13414
- cache: nextRecentFlowCache
13415
- } = mergeWorkspaceRecentFlowMetrics(transformedWorkspaceData, recentFlowCacheRef.current);
13416
- recentFlowCacheRef.current = nextRecentFlowCache;
13417
- mergedWorkspaceData.forEach((metric) => {
13370
+ transformedWorkspaceData.forEach((metric) => {
13418
13371
  workspaceMetricsStore.setOverview(metric);
13419
13372
  });
13420
13373
  const newMetricsState = {
13421
- workspaceMetrics: mergedWorkspaceData,
13374
+ workspaceMetrics: transformedWorkspaceData,
13422
13375
  lineMetrics: allLineMetrics || [],
13423
13376
  metadata: { hasFlowBuffers, idleTimeVlmByLine },
13424
13377
  efficiencyLegend: efficiencyLegend ?? DEFAULT_EFFICIENCY_LEGEND
@@ -16843,7 +16796,7 @@ var useActiveBreaks = (lineIds) => {
16843
16796
  const [isLoading, setIsLoading] = React141.useState(true);
16844
16797
  const [error, setError] = React141.useState(null);
16845
16798
  const supabase = useSupabase();
16846
- const parseTimeToMinutes3 = (timeStr) => {
16799
+ const parseTimeToMinutes4 = (timeStr) => {
16847
16800
  const [hours, minutes] = timeStr.split(":").map(Number);
16848
16801
  return hours * 60 + minutes;
16849
16802
  };
@@ -16852,8 +16805,8 @@ var useActiveBreaks = (lineIds) => {
16852
16805
  return now4.getHours() * 60 + now4.getMinutes();
16853
16806
  };
16854
16807
  const isTimeInBreak = (breakStart, breakEnd, currentMinutes) => {
16855
- const startMinutes = parseTimeToMinutes3(breakStart);
16856
- const endMinutes = parseTimeToMinutes3(breakEnd);
16808
+ const startMinutes = parseTimeToMinutes4(breakStart);
16809
+ const endMinutes = parseTimeToMinutes4(breakEnd);
16857
16810
  if (endMinutes < startMinutes) {
16858
16811
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
16859
16812
  } else {
@@ -16861,8 +16814,8 @@ var useActiveBreaks = (lineIds) => {
16861
16814
  }
16862
16815
  };
16863
16816
  const calculateBreakProgress = (breakStart, breakEnd, currentMinutes) => {
16864
- const startMinutes = parseTimeToMinutes3(breakStart);
16865
- const endMinutes = parseTimeToMinutes3(breakEnd);
16817
+ const startMinutes = parseTimeToMinutes4(breakStart);
16818
+ const endMinutes = parseTimeToMinutes4(breakEnd);
16866
16819
  let elapsedMinutes = 0;
16867
16820
  let remainingMinutes = 0;
16868
16821
  if (endMinutes < startMinutes) {
@@ -16880,8 +16833,8 @@ var useActiveBreaks = (lineIds) => {
16880
16833
  return { elapsedMinutes, remainingMinutes };
16881
16834
  };
16882
16835
  const isTimeInShift = (startTime, endTime, currentMinutes) => {
16883
- const startMinutes = parseTimeToMinutes3(startTime);
16884
- const endMinutes = parseTimeToMinutes3(endTime);
16836
+ const startMinutes = parseTimeToMinutes4(startTime);
16837
+ const endMinutes = parseTimeToMinutes4(endTime);
16885
16838
  if (endMinutes < startMinutes) {
16886
16839
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
16887
16840
  } else {
@@ -16941,8 +16894,8 @@ var useActiveBreaks = (lineIds) => {
16941
16894
  const endTime = breakItem.end || breakItem.endTime || "00:00";
16942
16895
  let duration = breakItem.duration || 0;
16943
16896
  if (!duration || duration === 0) {
16944
- const startMinutes = parseTimeToMinutes3(startTime);
16945
- const endMinutes = parseTimeToMinutes3(endTime);
16897
+ const startMinutes = parseTimeToMinutes4(startTime);
16898
+ const endMinutes = parseTimeToMinutes4(endTime);
16946
16899
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
16947
16900
  }
16948
16901
  return {
@@ -16958,8 +16911,8 @@ var useActiveBreaks = (lineIds) => {
16958
16911
  const endTime = breakItem.end || breakItem.endTime || "00:00";
16959
16912
  let duration = breakItem.duration || 0;
16960
16913
  if (!duration || duration === 0) {
16961
- const startMinutes = parseTimeToMinutes3(startTime);
16962
- const endMinutes = parseTimeToMinutes3(endTime);
16914
+ const startMinutes = parseTimeToMinutes4(startTime);
16915
+ const endMinutes = parseTimeToMinutes4(endTime);
16963
16916
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
16964
16917
  }
16965
16918
  return {
@@ -23040,6 +22993,16 @@ var createThrottledReload = (interval = 5e3, maxReloads = 3) => {
23040
22993
  };
23041
22994
  var throttledReloadDashboard = createThrottledReload(5e3, 3);
23042
22995
 
22996
+ // src/lib/utils/dev/localDevTestLogin.ts
22997
+ var isLoopbackHostname = (hostname) => {
22998
+ const normalized = String(hostname || "").trim().toLowerCase();
22999
+ return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
23000
+ };
23001
+ var shouldEnableLocalDevTestLogin = ({
23002
+ enabledFlag,
23003
+ hostname
23004
+ }) => enabledFlag && isLoopbackHostname(hostname);
23005
+
23043
23006
  // src/lib/utils/index.ts
23044
23007
  var formatIdleTime = (idleTimeInSeconds) => {
23045
23008
  if (!idleTimeInSeconds || idleTimeInSeconds <= 0) {
@@ -31224,12 +31187,16 @@ var LoginPage = ({
31224
31187
  onRateLimitCheck,
31225
31188
  logoSrc = optifye_logo_default,
31226
31189
  logoAlt = "Optifye",
31227
- brandName = "Optifye"
31190
+ brandName = "Optifye",
31191
+ showDevTestLogin = false,
31192
+ devTestLoginLabel = "Sign in as Test User",
31193
+ onDevTestLogin
31228
31194
  }) => {
31229
31195
  const [email, setEmail] = React141.useState("");
31230
31196
  const [otp, setOtp] = React141.useState("");
31231
31197
  const [step, setStep] = React141.useState("email");
31232
31198
  const [loading, setLoading] = React141.useState(false);
31199
+ const [devLoginLoading, setDevLoginLoading] = React141.useState(false);
31233
31200
  const [error, setError] = React141.useState(null);
31234
31201
  const [countdown, setCountdown] = React141.useState(0);
31235
31202
  const supabase = useSupabase();
@@ -31293,6 +31260,25 @@ var LoginPage = ({
31293
31260
  startCountdown();
31294
31261
  }
31295
31262
  };
31263
+ const handleDevTestLogin = async () => {
31264
+ if (!onDevTestLogin) {
31265
+ return;
31266
+ }
31267
+ setDevLoginLoading(true);
31268
+ setError(null);
31269
+ try {
31270
+ const result = await onDevTestLogin();
31271
+ const nextError = typeof result === "object" && result !== null && "error" in result ? result.error : null;
31272
+ if (typeof nextError === "string" && nextError.trim()) {
31273
+ setError(nextError);
31274
+ }
31275
+ } catch (err) {
31276
+ console.error("Dev test login failed:", err);
31277
+ setError("Dev test login unavailable.");
31278
+ } finally {
31279
+ setDevLoginLoading(false);
31280
+ }
31281
+ };
31296
31282
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-md w-full mx-4", children: [
31297
31283
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl shadow-xl border border-slate-200 p-8", children: [
31298
31284
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8", children: [
@@ -31333,7 +31319,7 @@ var LoginPage = ({
31333
31319
  "button",
31334
31320
  {
31335
31321
  type: "submit",
31336
- disabled: loading,
31322
+ disabled: loading || devLoginLoading,
31337
31323
  className: "w-full py-3 px-4 bg-slate-900 hover:bg-slate-800 focus:ring-2 focus:ring-slate-600 focus:ring-offset-2 text-white font-medium rounded-xl transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center",
31338
31324
  children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31339
31325
  /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "animate-spin -ml-1 mr-3 h-4 w-4 text-white", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
@@ -31343,7 +31329,31 @@ var LoginPage = ({
31343
31329
  "Sending..."
31344
31330
  ] }) : "Continue with Email"
31345
31331
  }
31346
- )
31332
+ ),
31333
+ showDevTestLogin && onDevTestLogin ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
31334
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
31335
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px flex-1 bg-slate-200" }),
31336
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium uppercase tracking-[0.2em] text-slate-400", children: "Local Dev" }),
31337
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px flex-1 bg-slate-200" })
31338
+ ] }),
31339
+ /* @__PURE__ */ jsxRuntime.jsx(
31340
+ "button",
31341
+ {
31342
+ type: "button",
31343
+ disabled: loading || devLoginLoading,
31344
+ onClick: () => void handleDevTestLogin(),
31345
+ className: "w-full py-3 px-4 bg-white hover:bg-slate-50 focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 text-slate-700 font-medium rounded-xl border border-slate-200 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center",
31346
+ children: devLoginLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31347
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "animate-spin -ml-1 mr-3 h-4 w-4 text-slate-700", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
31348
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
31349
+ /* @__PURE__ */ jsxRuntime.jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
31350
+ ] }),
31351
+ "Signing In..."
31352
+ ] }) : devTestLoginLabel
31353
+ }
31354
+ ),
31355
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-xs text-slate-500", children: "Localhost-only convenience login for the shared test account." })
31356
+ ] }) : null
31347
31357
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "space-y-6", onSubmit: handleVerifyOTP, children: [
31348
31358
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
31349
31359
  /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "otp", className: "block text-sm font-medium text-slate-700 mb-2 text-center", children: "Enter the 6-digit code sent to" }),
@@ -31368,7 +31378,7 @@ var LoginPage = ({
31368
31378
  "button",
31369
31379
  {
31370
31380
  type: "submit",
31371
- disabled: loading || otp.length !== 6,
31381
+ disabled: loading || devLoginLoading || otp.length !== 6,
31372
31382
  className: "w-full py-3 px-4 bg-slate-900 hover:bg-slate-800 focus:ring-2 focus:ring-slate-600 focus:ring-offset-2 text-white font-medium rounded-xl transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center",
31373
31383
  children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31374
31384
  /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "animate-spin -ml-1 mr-3 h-4 w-4 text-white", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
@@ -32209,31 +32219,35 @@ var LineChartComponent = ({
32209
32219
  ...restOfChartProps
32210
32220
  }) => {
32211
32221
  const containerRef = React141__namespace.default.useRef(null);
32212
- const [containerReady, setContainerReady] = React141__namespace.default.useState(false);
32213
- const themeConfig = useThemeConfig();
32214
- const { formatNumber } = useFormatNumber();
32222
+ const [dimensions, setDimensions] = React141__namespace.default.useState({ width: 0, height: 0 });
32223
+ const [hasValidData, setHasValidData] = React141__namespace.default.useState(false);
32215
32224
  React141__namespace.default.useEffect(() => {
32216
- const checkContainerDimensions = () => {
32217
- if (containerRef.current) {
32218
- const rect = containerRef.current.getBoundingClientRect();
32219
- if (rect.width > 0 && rect.height > 0) {
32220
- setContainerReady(true);
32221
- }
32222
- }
32223
- };
32224
- checkContainerDimensions();
32225
- const resizeObserver = new ResizeObserver(checkContainerDimensions);
32226
- if (containerRef.current) {
32227
- resizeObserver.observe(containerRef.current);
32225
+ const currentHasValidData = data && lines && lines.length > 0 && data.some(
32226
+ (item) => lines.some((line) => {
32227
+ const val = item[line.dataKey];
32228
+ return typeof val === "number" && val > 0;
32229
+ })
32230
+ );
32231
+ if (currentHasValidData && !hasValidData) {
32232
+ setHasValidData(true);
32228
32233
  }
32229
- const fallbackTimeout = setTimeout(() => {
32230
- setContainerReady(true);
32231
- }, 100);
32232
- return () => {
32233
- resizeObserver.disconnect();
32234
- clearTimeout(fallbackTimeout);
32235
- };
32234
+ }, [data, lines, hasValidData]);
32235
+ React141__namespace.default.useEffect(() => {
32236
+ if (!containerRef.current) return;
32237
+ const observer = new ResizeObserver((entries) => {
32238
+ const entry = entries[0];
32239
+ if (entry) {
32240
+ setDimensions({
32241
+ width: entry.contentRect.width,
32242
+ height: entry.contentRect.height
32243
+ });
32244
+ }
32245
+ });
32246
+ observer.observe(containerRef.current);
32247
+ return () => observer.disconnect();
32236
32248
  }, []);
32249
+ const themeConfig = useThemeConfig();
32250
+ const { formatNumber } = useFormatNumber();
32237
32251
  const yAxisTickFormatter = (value) => {
32238
32252
  return `${formatNumber(value)}${yAxisUnit || ""}`;
32239
32253
  };
@@ -32255,57 +32269,71 @@ var LineChartComponent = ({
32255
32269
  const gridStrokeColor = themeConfig?.gray?.["300"] || "#ccc";
32256
32270
  const axisTickFillColor = themeConfig?.gray?.["600"] || "#666";
32257
32271
  const axisStrokeColor = themeConfig?.gray?.["400"] || "#999";
32258
- const chartContent = /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data, margin: { top: 5, right: 30, left: 20, bottom: 20 }, ...restOfChartProps, children: [
32259
- showGrid && /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStrokeColor }),
32260
- /* @__PURE__ */ jsxRuntime.jsx(
32261
- recharts.XAxis,
32262
- {
32263
- dataKey: xAxisDataKey,
32264
- label: xAxisLabel ? { value: xAxisLabel, position: "insideBottom", offset: -10 } : void 0,
32265
- tickFormatter: xAxisTickFormatter,
32266
- tick: { fontSize: 12, fill: axisTickFillColor },
32267
- stroke: axisStrokeColor
32268
- }
32269
- ),
32270
- /* @__PURE__ */ jsxRuntime.jsx(
32271
- recharts.YAxis,
32272
- {
32273
- label: yAxisLabel ? { value: yAxisLabel, angle: -90, position: "insideLeft" } : void 0,
32274
- tickFormatter: yAxisTickFormatter,
32275
- domain: yAxisDomain,
32276
- tick: { fontSize: 12, fill: axisTickFillColor },
32277
- stroke: axisStrokeColor
32278
- }
32279
- ),
32280
- showTooltip && /* @__PURE__ */ jsxRuntime.jsx(
32281
- recharts.Tooltip,
32282
- {
32283
- formatter: tooltipFormatter || defaultTooltipFormatter,
32284
- labelFormatter: tooltipLabelFormatter,
32285
- itemStyle: { color: "#111827" },
32286
- cursor: { strokeDasharray: "3 3" }
32287
- }
32288
- ),
32289
- showLegend && /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { payload: legendPayload }),
32290
- lines.map((lineConfig, index) => {
32291
- const lineProps = {
32292
- ...lineConfig,
32293
- key: lineConfig.dataKey,
32294
- type: lineConfig.type || "monotone",
32295
- stroke: lineConfig.stroke || defaultColors[index % defaultColors.length],
32296
- activeDot: lineConfig.activeDot !== void 0 ? lineConfig.activeDot : { r: 6 }
32297
- };
32298
- return /* @__PURE__ */ jsxRuntime.jsx(recharts.Line, { ...lineProps, children: lineConfig.labelList && /* @__PURE__ */ jsxRuntime.jsx(
32299
- recharts.LabelList,
32300
- {
32301
- dataKey: lineConfig.dataKey,
32302
- position: "top",
32303
- formatter: (value) => formatNumber(value),
32304
- ...typeof lineConfig.labelList === "object" ? lineConfig.labelList : {}
32305
- }
32306
- ) });
32307
- })
32308
- ] });
32272
+ const renderChartContent = (chartWidth, chartHeight) => /* @__PURE__ */ jsxRuntime.jsxs(
32273
+ recharts.LineChart,
32274
+ {
32275
+ width: chartWidth,
32276
+ height: chartHeight,
32277
+ data,
32278
+ margin: { top: 5, right: 30, left: 20, bottom: 20 },
32279
+ ...restOfChartProps,
32280
+ children: [
32281
+ showGrid && /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStrokeColor }),
32282
+ /* @__PURE__ */ jsxRuntime.jsx(
32283
+ recharts.XAxis,
32284
+ {
32285
+ dataKey: xAxisDataKey,
32286
+ label: xAxisLabel ? { value: xAxisLabel, position: "insideBottom", offset: -10 } : void 0,
32287
+ tickFormatter: xAxisTickFormatter,
32288
+ tick: { fontSize: 12, fill: axisTickFillColor },
32289
+ stroke: axisStrokeColor
32290
+ }
32291
+ ),
32292
+ /* @__PURE__ */ jsxRuntime.jsx(
32293
+ recharts.YAxis,
32294
+ {
32295
+ label: yAxisLabel ? { value: yAxisLabel, angle: -90, position: "insideLeft" } : void 0,
32296
+ tickFormatter: yAxisTickFormatter,
32297
+ domain: yAxisDomain,
32298
+ tick: { fontSize: 12, fill: axisTickFillColor },
32299
+ stroke: axisStrokeColor
32300
+ }
32301
+ ),
32302
+ showTooltip && /* @__PURE__ */ jsxRuntime.jsx(
32303
+ recharts.Tooltip,
32304
+ {
32305
+ formatter: tooltipFormatter || defaultTooltipFormatter,
32306
+ labelFormatter: tooltipLabelFormatter,
32307
+ itemStyle: { color: "#111827" },
32308
+ cursor: { strokeDasharray: "3 3" }
32309
+ }
32310
+ ),
32311
+ showLegend && /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { payload: legendPayload }),
32312
+ lines.map((lineConfig, index) => {
32313
+ const lineProps = {
32314
+ ...lineConfig,
32315
+ key: lineConfig.dataKey,
32316
+ type: lineConfig.type || "monotone",
32317
+ stroke: lineConfig.stroke || defaultColors[index % defaultColors.length],
32318
+ activeDot: lineConfig.activeDot !== void 0 ? lineConfig.activeDot : { r: 6 },
32319
+ isAnimationActive: true,
32320
+ animationDuration: 1500,
32321
+ animationBegin: 300
32322
+ };
32323
+ return /* @__PURE__ */ jsxRuntime.jsx(recharts.Line, { ...lineProps, children: lineConfig.labelList && /* @__PURE__ */ jsxRuntime.jsx(
32324
+ recharts.LabelList,
32325
+ {
32326
+ dataKey: lineConfig.dataKey,
32327
+ position: "top",
32328
+ formatter: (value) => formatNumber(value),
32329
+ ...typeof lineConfig.labelList === "object" ? lineConfig.labelList : {}
32330
+ }
32331
+ ) });
32332
+ })
32333
+ ]
32334
+ },
32335
+ hasValidData ? "valid" : "empty"
32336
+ );
32309
32337
  if (responsive) {
32310
32338
  return /* @__PURE__ */ jsxRuntime.jsx(
32311
32339
  "div",
@@ -32313,11 +32341,20 @@ var LineChartComponent = ({
32313
32341
  ref: containerRef,
32314
32342
  className: clsx(fillContainer ? "w-full h-full" : "w-full h-auto", className),
32315
32343
  style: fillContainer ? { height: "100%", minHeight: "50px", minWidth: "100px" } : { aspectRatio: `${aspect}/1`, minHeight: "50px", minWidth: "100px" },
32316
- children: containerReady ? /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: chartContent }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) })
32344
+ children: /* @__PURE__ */ jsxRuntime.jsx(
32345
+ motion.div,
32346
+ {
32347
+ initial: { opacity: 0 },
32348
+ animate: { opacity: 1 },
32349
+ transition: { duration: 0.5 },
32350
+ className: "w-full h-full",
32351
+ children: dimensions.width > 0 && dimensions.height > 0 && renderChartContent(dimensions.width, dimensions.height)
32352
+ }
32353
+ )
32317
32354
  }
32318
32355
  );
32319
32356
  }
32320
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("w-full", className), children: chartContent });
32357
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("w-full", className), children: renderChartContent(restOfChartProps.width, restOfChartProps.height) });
32321
32358
  };
32322
32359
  var LineChart = React141__namespace.default.memo(LineChartComponent, (prevProps, nextProps) => {
32323
32360
  if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect || JSON.stringify(prevProps.yAxisDomain) !== JSON.stringify(nextProps.yAxisDomain)) {
@@ -32585,14 +32622,35 @@ var CycleTimeOverTimeChart = ({
32585
32622
  }) => {
32586
32623
  const MAX_DATA_POINTS = 40;
32587
32624
  const containerRef = React141__namespace.default.useRef(null);
32588
- const [containerReady, setContainerReady] = React141__namespace.default.useState(false);
32589
- const parseTimeToMinutes3 = (value) => {
32625
+ const [dimensions, setDimensions] = React141__namespace.default.useState({ width: 0, height: 0 });
32626
+ const [hasValidData, setHasValidData] = React141__namespace.default.useState(false);
32627
+ React141__namespace.default.useEffect(() => {
32628
+ const currentHasValidData = data && data.some((val) => val !== null && val > 0);
32629
+ if (currentHasValidData && !hasValidData) {
32630
+ setHasValidData(true);
32631
+ }
32632
+ }, [data, hasValidData]);
32633
+ React141__namespace.default.useEffect(() => {
32634
+ if (!containerRef.current) return;
32635
+ const observer = new ResizeObserver((entries) => {
32636
+ const entry = entries[0];
32637
+ if (entry) {
32638
+ setDimensions({
32639
+ width: entry.contentRect.width,
32640
+ height: entry.contentRect.height
32641
+ });
32642
+ }
32643
+ });
32644
+ observer.observe(containerRef.current);
32645
+ return () => observer.disconnect();
32646
+ }, []);
32647
+ const parseTimeToMinutes4 = (value) => {
32590
32648
  const [hours, minutes] = value.split(":").map(Number);
32591
32649
  if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return 0;
32592
32650
  return hours * 60 + minutes;
32593
32651
  };
32594
32652
  const formatHourLabel = (slotIndex) => {
32595
- const baseMinutes = parseTimeToMinutes3(shiftStart);
32653
+ const baseMinutes = parseTimeToMinutes4(shiftStart);
32596
32654
  const absoluteMinutes = baseMinutes + slotIndex * 60;
32597
32655
  const hour24 = Math.floor(absoluteMinutes % (24 * 60) / 60);
32598
32656
  const ampm = hour24 >= 12 ? "PM" : "AM";
@@ -32611,52 +32669,7 @@ var CycleTimeOverTimeChart = ({
32611
32669
  const displayData = getDisplayData(data);
32612
32670
  const DURATION = displayData.length;
32613
32671
  const effectiveDatasetKey = datasetKey || `cycle-time:${xAxisMode}`;
32614
- const [animatedDatasetKey, setAnimatedDatasetKey] = React141__namespace.default.useState(null);
32615
- const shouldAnimate = animatedDatasetKey !== effectiveDatasetKey;
32616
- const handleAnimationEnd = React141__namespace.default.useCallback(() => {
32617
- setAnimatedDatasetKey((currentValue) => currentValue === effectiveDatasetKey ? currentValue : effectiveDatasetKey);
32618
- }, [effectiveDatasetKey]);
32619
32672
  const finalData = displayData;
32620
- React141__namespace.default.useEffect(() => {
32621
- const containerNode = containerRef.current;
32622
- if (!containerNode) {
32623
- setContainerReady(true);
32624
- return void 0;
32625
- }
32626
- let frameId = null;
32627
- let resizeObserver = null;
32628
- const checkContainerDimensions = () => {
32629
- const rect = containerNode.getBoundingClientRect();
32630
- const isReady = rect.width > 0 && rect.height > 0;
32631
- if (isReady) {
32632
- setContainerReady(true);
32633
- }
32634
- return isReady;
32635
- };
32636
- if (checkContainerDimensions()) {
32637
- return void 0;
32638
- }
32639
- frameId = window.requestAnimationFrame(() => {
32640
- checkContainerDimensions();
32641
- });
32642
- if (typeof ResizeObserver !== "undefined") {
32643
- resizeObserver = new ResizeObserver(() => {
32644
- if (checkContainerDimensions() && resizeObserver) {
32645
- resizeObserver.disconnect();
32646
- resizeObserver = null;
32647
- }
32648
- });
32649
- resizeObserver.observe(containerNode);
32650
- } else {
32651
- setContainerReady(true);
32652
- }
32653
- return () => {
32654
- if (frameId !== null) {
32655
- window.cancelAnimationFrame(frameId);
32656
- }
32657
- resizeObserver?.disconnect();
32658
- };
32659
- }, []);
32660
32673
  const labelInterval = React141__namespace.default.useMemo(() => {
32661
32674
  if (xAxisMode === "hourly") {
32662
32675
  return Math.max(1, Math.ceil(DURATION / 8));
@@ -32698,16 +32711,162 @@ var CycleTimeOverTimeChart = ({
32698
32711
  return `${minutes} minutes ${seconds} seconds ago`;
32699
32712
  }
32700
32713
  };
32714
+ const getNumericValue = React141__namespace.default.useCallback((value) => typeof value === "number" && Number.isFinite(value) ? value : null, []);
32715
+ const renderChartTooltip = React141__namespace.default.useCallback((tooltipProps) => {
32716
+ const { active, payload } = tooltipProps;
32717
+ if (!active || !Array.isArray(payload) || payload.length === 0) {
32718
+ return null;
32719
+ }
32720
+ const visibleEntries = payload.filter((entry) => getNumericValue(entry.value) !== null);
32721
+ if (!visibleEntries.length) {
32722
+ return null;
32723
+ }
32724
+ return /* @__PURE__ */ jsxRuntime.jsxs(
32725
+ "div",
32726
+ {
32727
+ style: {
32728
+ backgroundColor: "white",
32729
+ border: "none",
32730
+ borderRadius: "8px",
32731
+ boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
32732
+ padding: "8px 12px",
32733
+ fontSize: "13px"
32734
+ },
32735
+ children: [
32736
+ /* @__PURE__ */ jsxRuntime.jsx(
32737
+ "div",
32738
+ {
32739
+ style: {
32740
+ color: "#374151",
32741
+ fontWeight: 600,
32742
+ marginBottom: "4px"
32743
+ },
32744
+ children: payload[0]?.payload?.tooltip || ""
32745
+ }
32746
+ ),
32747
+ visibleEntries.map((entry) => {
32748
+ const numericValue = getNumericValue(entry.value);
32749
+ if (numericValue === null) {
32750
+ return null;
32751
+ }
32752
+ return /* @__PURE__ */ jsxRuntime.jsx(
32753
+ "div",
32754
+ {
32755
+ style: {
32756
+ color: "#4B5563",
32757
+ padding: "2px 0"
32758
+ },
32759
+ children: entry.name === "idleMinutes" ? `Idle Time: ${numericValue.toFixed(0)} minutes` : `Cycle Time: ${numericValue.toFixed(1)} seconds`
32760
+ },
32761
+ `${entry.name}-${numericValue}`
32762
+ );
32763
+ })
32764
+ ]
32765
+ }
32766
+ );
32767
+ }, [getNumericValue]);
32768
+ const renderCycleDot = React141__namespace.default.useCallback((props) => {
32769
+ const { cx: cx2, cy, payload } = props;
32770
+ const cycleTime = getNumericValue(payload?.cycleTime);
32771
+ if (cycleTime === null) {
32772
+ return /* @__PURE__ */ jsxRuntime.jsx("g", {});
32773
+ }
32774
+ return /* @__PURE__ */ jsxRuntime.jsx(
32775
+ "circle",
32776
+ {
32777
+ cx: cx2,
32778
+ cy,
32779
+ r: 4,
32780
+ fill: cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32781
+ stroke: "#fff",
32782
+ strokeWidth: 1,
32783
+ style: {
32784
+ filter: "brightness(1)",
32785
+ transition: "filter 0.3s ease, transform 0.3s ease",
32786
+ cursor: "pointer"
32787
+ },
32788
+ onMouseEnter: (e) => {
32789
+ const target = e.target;
32790
+ target.style.filter = "brightness(1.2)";
32791
+ target.style.transform = "scale(1.2)";
32792
+ },
32793
+ onMouseLeave: (e) => {
32794
+ const target = e.target;
32795
+ target.style.filter = "brightness(1)";
32796
+ target.style.transform = "scale(1)";
32797
+ }
32798
+ }
32799
+ );
32800
+ }, [getNumericValue, idealCycleTime]);
32801
+ const renderCycleActiveDot = React141__namespace.default.useCallback((props) => {
32802
+ const { cx: cx2, cy, payload } = props;
32803
+ const cycleTime = getNumericValue(payload?.cycleTime);
32804
+ if (cycleTime === null) {
32805
+ return /* @__PURE__ */ jsxRuntime.jsx("g", {});
32806
+ }
32807
+ return /* @__PURE__ */ jsxRuntime.jsx(
32808
+ "circle",
32809
+ {
32810
+ cx: cx2,
32811
+ cy,
32812
+ r: 6,
32813
+ fill: cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32814
+ stroke: "#fff",
32815
+ strokeWidth: 2,
32816
+ style: {
32817
+ filter: "drop-shadow(0 0 2px rgba(0,0,0,0.2))"
32818
+ }
32819
+ }
32820
+ );
32821
+ }, [getNumericValue, idealCycleTime]);
32822
+ const renderIdleDot = React141__namespace.default.useCallback((props) => {
32823
+ const { cx: cx2, cy, payload } = props;
32824
+ const idleMinutes = getNumericValue(payload?.idleMinutes);
32825
+ if (idleMinutes === null) {
32826
+ return /* @__PURE__ */ jsxRuntime.jsx("g", {});
32827
+ }
32828
+ return /* @__PURE__ */ jsxRuntime.jsx(
32829
+ "circle",
32830
+ {
32831
+ cx: cx2,
32832
+ cy,
32833
+ r: 4,
32834
+ fill: "#f59e0b",
32835
+ stroke: "#fff",
32836
+ strokeWidth: 1
32837
+ }
32838
+ );
32839
+ }, [getNumericValue]);
32840
+ const renderIdleActiveDot = React141__namespace.default.useCallback((props) => {
32841
+ const { cx: cx2, cy, payload } = props;
32842
+ const idleMinutes = getNumericValue(payload?.idleMinutes);
32843
+ if (idleMinutes === null) {
32844
+ return /* @__PURE__ */ jsxRuntime.jsx("g", {});
32845
+ }
32846
+ return /* @__PURE__ */ jsxRuntime.jsx(
32847
+ "circle",
32848
+ {
32849
+ cx: cx2,
32850
+ cy,
32851
+ r: 6,
32852
+ fill: "#f59e0b",
32853
+ stroke: "#fff",
32854
+ strokeWidth: 2
32855
+ }
32856
+ );
32857
+ }, [getNumericValue]);
32701
32858
  const chartData = React141__namespace.default.useMemo(() => Array.from({ length: DURATION }, (_, i) => {
32859
+ const cycleTime = getNumericValue(finalData[i]);
32860
+ const idleMinutes = showIdleTime ? getNumericValue(idleTimeData[i]) : null;
32702
32861
  return {
32703
32862
  timeIndex: i,
32704
32863
  label: formatTimeLabel(i),
32705
32864
  tooltip: formatTooltipTime(i),
32706
- cycleTime: finalData[i] || 0,
32707
- idleMinutes: showIdleTime && idleTimeData && idleTimeData[i] || 0,
32708
- color: (finalData[i] || 0) <= idealCycleTime ? "#00AB45" : "#E34329"
32865
+ cycleTime,
32866
+ idleMinutes,
32867
+ color: cycleTime !== null && cycleTime <= idealCycleTime ? "#00AB45" : "#E34329"
32709
32868
  };
32710
- }), [DURATION, finalData, showIdleTime, idleTimeData, idealCycleTime]);
32869
+ }), [DURATION, finalData, showIdleTime, idleTimeData, idealCycleTime, getNumericValue]);
32711
32870
  const renderLegend = () => {
32712
32871
  if (!showIdleTime) return null;
32713
32872
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-start text-[10px] font-bold text-gray-500 mb-6 tracking-[0.05em] gap-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
@@ -32718,215 +32877,154 @@ var CycleTimeOverTimeChart = ({
32718
32877
  return /* @__PURE__ */ jsxRuntime.jsxs(
32719
32878
  "div",
32720
32879
  {
32721
- ref: containerRef,
32722
32880
  className: `w-full h-full min-w-0 flex flex-col relative pb-2 ${className}`,
32723
32881
  style: { minHeight: "200px", minWidth: 0 },
32724
32882
  children: [
32725
32883
  renderLegend(),
32726
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 w-full", children: containerReady ? /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
32727
- recharts.LineChart,
32884
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 w-full", ref: containerRef, children: /* @__PURE__ */ jsxRuntime.jsx(
32885
+ motion.div,
32728
32886
  {
32729
- data: chartData,
32730
- margin: {
32731
- top: 5,
32732
- right: 30,
32733
- bottom: 25,
32734
- left: 10
32735
- },
32736
- children: [
32737
- /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
32738
- /* @__PURE__ */ jsxRuntime.jsx(
32739
- recharts.XAxis,
32740
- {
32741
- dataKey: "label",
32742
- tick: { fontSize: 11 },
32743
- interval: 0,
32744
- angle: xAxisMode === "hourly" ? 0 : -30,
32745
- textAnchor: xAxisMode === "hourly" ? "middle" : "end",
32746
- tickMargin: xAxisMode === "hourly" ? 8 : 15,
32747
- height: xAxisMode === "hourly" ? 40 : 60
32748
- }
32749
- ),
32750
- /* @__PURE__ */ jsxRuntime.jsx(
32751
- recharts.YAxis,
32752
- {
32753
- tickMargin: 8,
32754
- width: 45,
32755
- yAxisId: "cycle",
32756
- domain: ["auto", "auto"],
32757
- ticks: [0, idealCycleTime, ...Array.from({ length: 4 }, (_, i) => (i + 1) * Math.ceil(idealCycleTime / 2))].sort((a, b) => a - b),
32758
- tickFormatter: (value) => String(value),
32759
- tick: (props) => {
32760
- const { x, y, payload } = props;
32761
- const displayValue = typeof payload.value === "number" ? payload.value.toFixed(1) : String(payload.value);
32762
- return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
32763
- "text",
32764
- {
32765
- x: 0,
32766
- y: 0,
32767
- dy: 4,
32768
- textAnchor: "end",
32769
- fill: payload.value === idealCycleTime ? "#E34329" : "#666",
32770
- fontSize: 12,
32771
- fontWeight: payload.value === idealCycleTime ? "bold" : "normal",
32772
- children: displayValue
32773
- },
32774
- `tick-${payload.value}-${x}-${y}`
32775
- ) });
32776
- }
32777
- }
32778
- ),
32779
- showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
32780
- recharts.YAxis,
32781
- {
32782
- yAxisId: "idle",
32783
- orientation: "right",
32784
- tickMargin: 8,
32785
- width: 35,
32786
- domain: [0, 60],
32787
- tickFormatter: (value) => `${value}m`,
32788
- tick: { fontSize: 11, fill: "#f59e0b" },
32789
- axisLine: false,
32790
- tickLine: false
32791
- }
32792
- ),
32793
- /* @__PURE__ */ jsxRuntime.jsx(
32794
- recharts.Tooltip,
32795
- {
32796
- cursor: { stroke: "#E5E7EB", strokeWidth: 1 },
32797
- contentStyle: {
32798
- backgroundColor: "white",
32799
- border: "none",
32800
- borderRadius: "8px",
32801
- boxShadow: "0 4px 12px rgba(0,0,0,0.1)",
32802
- padding: "8px 12px",
32803
- fontSize: "13px"
32804
- },
32805
- labelStyle: {
32806
- color: "#374151",
32807
- fontWeight: 600,
32808
- marginBottom: "4px"
32809
- },
32810
- itemStyle: {
32811
- color: "#4B5563",
32812
- padding: "2px 0"
32813
- },
32814
- labelFormatter: (label, payload) => {
32815
- if (payload && payload[0]) {
32816
- return payload[0].payload.tooltip;
32817
- }
32818
- return label;
32819
- },
32820
- formatter: (value, name) => {
32821
- const numValue = typeof value === "number" ? value : Number(value);
32822
- if (name === "idleMinutes") {
32823
- return [`${numValue.toFixed(0)} minutes`, "Idle Time"];
32887
+ initial: { opacity: 0 },
32888
+ animate: { opacity: 1 },
32889
+ transition: { duration: 0.5 },
32890
+ className: "w-full h-full",
32891
+ children: dimensions.width > 0 && dimensions.height > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
32892
+ recharts.LineChart,
32893
+ {
32894
+ width: dimensions.width,
32895
+ height: dimensions.height,
32896
+ data: chartData,
32897
+ margin: {
32898
+ top: 5,
32899
+ right: 30,
32900
+ bottom: 25,
32901
+ left: 10
32902
+ },
32903
+ children: [
32904
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
32905
+ /* @__PURE__ */ jsxRuntime.jsx(
32906
+ recharts.XAxis,
32907
+ {
32908
+ dataKey: "label",
32909
+ tick: { fontSize: 11 },
32910
+ interval: 0,
32911
+ angle: xAxisMode === "hourly" ? 0 : -30,
32912
+ textAnchor: xAxisMode === "hourly" ? "middle" : "end",
32913
+ tickMargin: xAxisMode === "hourly" ? 8 : 15,
32914
+ height: xAxisMode === "hourly" ? 40 : 60
32824
32915
  }
32825
- return [`${numValue.toFixed(1)} seconds`, "Cycle Time"];
32826
- },
32827
- animationDuration: 200
32828
- }
32829
- ),
32830
- /* @__PURE__ */ jsxRuntime.jsx(
32831
- recharts.ReferenceLine,
32832
- {
32833
- y: idealCycleTime,
32834
- yAxisId: "cycle",
32835
- stroke: "#E34329",
32836
- strokeDasharray: "3 3",
32837
- strokeWidth: 2,
32838
- label: {
32839
- position: "right",
32840
- value: `${idealCycleTime.toFixed(1)}s`,
32841
- fill: "#E34329",
32842
- fontSize: 12,
32843
- fontWeight: 500
32844
- }
32845
- }
32846
- ),
32847
- /* @__PURE__ */ jsxRuntime.jsx(
32848
- recharts.Line,
32849
- {
32850
- type: "monotone",
32851
- yAxisId: "cycle",
32852
- dataKey: "cycleTime",
32853
- stroke: "#3B82F6",
32854
- strokeWidth: 2,
32855
- dot: (props) => {
32856
- const { cx: cx2, cy, payload } = props;
32857
- return /* @__PURE__ */ jsxRuntime.jsx(
32858
- "circle",
32859
- {
32860
- cx: cx2,
32861
- cy,
32862
- r: 4,
32863
- fill: payload.cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32864
- stroke: "#fff",
32865
- strokeWidth: 1,
32866
- style: {
32867
- filter: "brightness(1)",
32868
- transition: "filter 0.3s ease, transform 0.3s ease",
32869
- cursor: "pointer"
32870
- },
32871
- onMouseEnter: (e) => {
32872
- const target = e.target;
32873
- target.style.filter = "brightness(1.2)";
32874
- target.style.transform = "scale(1.2)";
32875
- },
32876
- onMouseLeave: (e) => {
32877
- const target = e.target;
32878
- target.style.filter = "brightness(1)";
32879
- target.style.transform = "scale(1)";
32880
- }
32916
+ ),
32917
+ /* @__PURE__ */ jsxRuntime.jsx(
32918
+ recharts.YAxis,
32919
+ {
32920
+ tickMargin: 8,
32921
+ width: 45,
32922
+ yAxisId: "cycle",
32923
+ domain: ["auto", "auto"],
32924
+ ticks: [0, idealCycleTime, ...Array.from({ length: 4 }, (_, i) => (i + 1) * Math.ceil(idealCycleTime / 2))].sort((a, b) => a - b),
32925
+ tickFormatter: (value) => String(value),
32926
+ tick: (props) => {
32927
+ const { x, y, payload } = props;
32928
+ const displayValue = typeof payload.value === "number" ? payload.value.toFixed(1) : String(payload.value);
32929
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
32930
+ "text",
32931
+ {
32932
+ x: 0,
32933
+ y: 0,
32934
+ dy: 4,
32935
+ textAnchor: "end",
32936
+ fill: payload.value === idealCycleTime ? "#E34329" : "#666",
32937
+ fontSize: 12,
32938
+ fontWeight: payload.value === idealCycleTime ? "bold" : "normal",
32939
+ children: displayValue
32940
+ },
32941
+ `tick-${payload.value}-${x}-${y}`
32942
+ ) });
32881
32943
  }
32882
- );
32883
- },
32884
- activeDot: (props) => {
32885
- const { cx: cx2, cy, payload } = props;
32886
- return /* @__PURE__ */ jsxRuntime.jsx(
32887
- "circle",
32888
- {
32889
- cx: cx2,
32890
- cy,
32891
- r: 6,
32892
- fill: payload.cycleTime <= idealCycleTime ? "#00AB45" : "#E34329",
32893
- stroke: "#fff",
32894
- strokeWidth: 2,
32895
- style: {
32896
- filter: "drop-shadow(0 0 2px rgba(0,0,0,0.2))"
32897
- }
32944
+ }
32945
+ ),
32946
+ showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
32947
+ recharts.YAxis,
32948
+ {
32949
+ yAxisId: "idle",
32950
+ orientation: "right",
32951
+ tickMargin: 8,
32952
+ width: 35,
32953
+ domain: [0, 60],
32954
+ tickFormatter: (value) => `${value}m`,
32955
+ tick: { fontSize: 11, fill: "#f59e0b" },
32956
+ axisLine: false,
32957
+ tickLine: false
32958
+ }
32959
+ ),
32960
+ /* @__PURE__ */ jsxRuntime.jsx(
32961
+ recharts.Tooltip,
32962
+ {
32963
+ cursor: { stroke: "#E5E7EB", strokeWidth: 1 },
32964
+ content: renderChartTooltip,
32965
+ animationDuration: 200
32966
+ }
32967
+ ),
32968
+ /* @__PURE__ */ jsxRuntime.jsx(
32969
+ recharts.ReferenceLine,
32970
+ {
32971
+ y: idealCycleTime,
32972
+ yAxisId: "cycle",
32973
+ stroke: "#E34329",
32974
+ strokeDasharray: "3 3",
32975
+ strokeWidth: 2,
32976
+ label: {
32977
+ position: "right",
32978
+ value: `${idealCycleTime.toFixed(1)}s`,
32979
+ fill: "#E34329",
32980
+ fontSize: 12,
32981
+ fontWeight: 500
32898
32982
  }
32899
- );
32900
- },
32901
- isAnimationActive: shouldAnimate,
32902
- animationBegin: 0,
32903
- animationDuration: 1200,
32904
- animationEasing: "ease-out",
32905
- onAnimationEnd: handleAnimationEnd
32906
- },
32907
- `${effectiveDatasetKey}:cycle`
32908
- ),
32909
- showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
32910
- recharts.Line,
32911
- {
32912
- type: "monotone",
32913
- yAxisId: "idle",
32914
- dataKey: "idleMinutes",
32915
- stroke: "#f59e0b",
32916
- strokeWidth: 2,
32917
- strokeDasharray: "4 4",
32918
- dot: { r: 4, fill: "#f59e0b", stroke: "#fff", strokeWidth: 1 },
32919
- activeDot: { r: 6, fill: "#f59e0b", stroke: "#fff", strokeWidth: 2 },
32920
- isAnimationActive: shouldAnimate,
32921
- animationBegin: 0,
32922
- animationDuration: 1200,
32923
- animationEasing: "ease-out"
32924
- },
32925
- `${effectiveDatasetKey}:idle`
32926
- )
32927
- ]
32983
+ }
32984
+ ),
32985
+ /* @__PURE__ */ jsxRuntime.jsx(
32986
+ recharts.Line,
32987
+ {
32988
+ type: "monotone",
32989
+ yAxisId: "cycle",
32990
+ dataKey: "cycleTime",
32991
+ stroke: "#3B82F6",
32992
+ strokeWidth: 2,
32993
+ connectNulls: false,
32994
+ dot: renderCycleDot,
32995
+ activeDot: renderCycleActiveDot,
32996
+ isAnimationActive: true,
32997
+ animationBegin: 300,
32998
+ animationDuration: 1500,
32999
+ animationEasing: "ease-out"
33000
+ },
33001
+ `${effectiveDatasetKey}:cycle`
33002
+ ),
33003
+ showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
33004
+ recharts.Line,
33005
+ {
33006
+ type: "monotone",
33007
+ yAxisId: "idle",
33008
+ dataKey: "idleMinutes",
33009
+ stroke: "#f59e0b",
33010
+ strokeWidth: 2,
33011
+ strokeDasharray: "4 4",
33012
+ connectNulls: false,
33013
+ dot: renderIdleDot,
33014
+ activeDot: renderIdleActiveDot,
33015
+ isAnimationActive: true,
33016
+ animationBegin: 300,
33017
+ animationDuration: 1500,
33018
+ animationEasing: "ease-out"
33019
+ },
33020
+ `${effectiveDatasetKey}:idle`
33021
+ )
33022
+ ]
33023
+ },
33024
+ hasValidData ? "valid" : "empty"
33025
+ )
32928
33026
  }
32929
- ) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center bg-gray-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-500 text-sm", children: "Loading chart..." }) }) })
33027
+ ) })
32930
33028
  ]
32931
33029
  }
32932
33030
  );
@@ -33776,10 +33874,10 @@ var HourlyOutputChart = React141__namespace.default.memo(HourlyOutputChartCompon
33776
33874
  HourlyOutputChart.displayName = "HourlyOutputChart";
33777
33875
 
33778
33876
  // src/components/dashboard/grid/videoGridMetricUtils.ts
33779
- var VIDEO_GRID_LEGEND_LABEL = "Flow";
33877
+ var VIDEO_GRID_LEGEND_LABEL = "7 Minute Efficiency";
33780
33878
  var MAP_GRID_LEGEND_LABEL = "Efficiency";
33781
- var MIXED_VIDEO_GRID_LEGEND_LABEL = "Flow / Efficiency";
33782
- var isFiniteNumber2 = (value) => typeof value === "number" && Number.isFinite(value);
33879
+ var MIXED_VIDEO_GRID_LEGEND_LABEL = "Efficiency";
33880
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
33783
33881
  var isVideoGridRecentFlowEnabled = (workspace) => isRecentFlowVideoGridMetricMode(
33784
33882
  workspace.video_grid_metric_mode,
33785
33883
  workspace.assembly_enabled === true
@@ -33788,11 +33886,11 @@ var isVideoGridWipGated = (workspace) => isWipGatedVideoGridMetricMode(
33788
33886
  workspace.video_grid_metric_mode,
33789
33887
  workspace.assembly_enabled === true
33790
33888
  );
33791
- var hasVideoGridRecentFlow = (workspace) => isVideoGridRecentFlowEnabled(workspace) && isFiniteNumber2(workspace.recent_flow_percent);
33889
+ var hasVideoGridRecentFlow = (workspace) => isVideoGridRecentFlowEnabled(workspace) && isFiniteNumber(workspace.recent_flow_percent);
33792
33890
  var isVideoGridRecentFlowUnavailable = (workspace) => isVideoGridRecentFlowEnabled(workspace) && !hasVideoGridRecentFlow(workspace);
33793
33891
  var getVideoGridMetricValue = (workspace) => {
33794
33892
  const recentFlowPercent = workspace.recent_flow_percent;
33795
- if (hasVideoGridRecentFlow(workspace) && isFiniteNumber2(recentFlowPercent)) {
33893
+ if (hasVideoGridRecentFlow(workspace) && isFiniteNumber(recentFlowPercent)) {
33796
33894
  return recentFlowPercent;
33797
33895
  }
33798
33896
  if (isVideoGridRecentFlowUnavailable(workspace)) {
@@ -33803,7 +33901,7 @@ var getVideoGridMetricValue = (workspace) => {
33803
33901
  var hasIncomingWipMapping = (workspace) => Boolean(workspace.incoming_wip_buffer_name);
33804
33902
  var getVideoGridBaseColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33805
33903
  const metricValue = getVideoGridMetricValue(workspace);
33806
- if (!isFiniteNumber2(metricValue)) {
33904
+ if (!isFiniteNumber(metricValue)) {
33807
33905
  return "neutral";
33808
33906
  }
33809
33907
  return getEfficiencyColor(metricValue, legend);
@@ -33818,7 +33916,7 @@ var isLowWipGreenOverride = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33818
33916
  if (!hasIncomingWipMapping(workspace)) {
33819
33917
  return false;
33820
33918
  }
33821
- return isFiniteNumber2(workspace.incoming_wip_current) && workspace.incoming_wip_current <= 1;
33919
+ return isFiniteNumber(workspace.incoming_wip_current) && workspace.incoming_wip_current <= 1;
33822
33920
  };
33823
33921
  var toMinuteBucket = (minuteBucket) => Number.isFinite(minuteBucket) ? Math.floor(minuteBucket) : Math.floor(Date.now() / 6e4);
33824
33922
  var getEffectiveFlowMinuteBucket = (workspace) => {
@@ -33860,7 +33958,7 @@ var getVideoGridColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) =>
33860
33958
  if (!hasIncomingWipMapping(workspace)) {
33861
33959
  return baseColor;
33862
33960
  }
33863
- if (!isFiniteNumber2(workspace.incoming_wip_current)) {
33961
+ if (!isFiniteNumber(workspace.incoming_wip_current)) {
33864
33962
  return "neutral";
33865
33963
  }
33866
33964
  if (isLowWipGreenOverride(workspace, legend)) {
@@ -34052,7 +34150,7 @@ var VideoCard = React141__namespace.default.memo(({
34052
34150
  }
34053
34151
  );
34054
34152
  }, (prevProps, nextProps) => {
34055
- if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_mode !== nextProps.workspace.recent_flow_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
34153
+ if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
34056
34154
  return false;
34057
34155
  }
34058
34156
  if (prevProps.workspace.workspace_uuid !== nextProps.workspace.workspace_uuid || prevProps.workspace.workspace_name !== nextProps.workspace.workspace_name || prevProps.workspace.line_id !== nextProps.workspace.line_id) {
@@ -34644,8 +34742,20 @@ var CardFooter2 = (props) => {
34644
34742
  return /* @__PURE__ */ jsxRuntime.jsx(RegisteredCardFooter, { ...props });
34645
34743
  };
34646
34744
 
34745
+ // src/lib/utils/workspaceDetailCycleTime.ts
34746
+ var resolveWorkspaceDetailActionFamily = (workspace) => {
34747
+ if (workspace?.action_family === "assembly" || workspace?.action_family === "output" || workspace?.action_family === "other") {
34748
+ return workspace.action_family;
34749
+ }
34750
+ if (workspace?.action_type === "assembly" || workspace?.action_type === "output") {
34751
+ return workspace.action_type;
34752
+ }
34753
+ return null;
34754
+ };
34755
+ var shouldUseAssemblyCycleTimeLayout = (workspace) => workspace?.line_assembly_enabled === true && resolveWorkspaceDetailActionFamily(workspace) === "assembly";
34756
+
34647
34757
  // src/components/dashboard/workspace/workspaceDetailCardRules.ts
34648
- var shouldHideWorkspaceEfficiencyCard = (workspace) => workspace?.line_assembly_enabled === true && workspace?.action_type === "assembly";
34758
+ var shouldHideWorkspaceEfficiencyCard = (workspace) => shouldUseAssemblyCycleTimeLayout(workspace);
34649
34759
  var WorkspaceMetricCardsImpl = ({
34650
34760
  workspace,
34651
34761
  className,
@@ -35354,6 +35464,37 @@ var getShiftElapsedMinutes = ({
35354
35464
  const elapsed = dateFns.differenceInMinutes(now4, shiftStartDate);
35355
35465
  return Math.min(Math.max(elapsed, 0), shiftMinutes);
35356
35466
  };
35467
+ var maskFutureHourlySeries = ({
35468
+ data,
35469
+ shiftStart,
35470
+ shiftEnd,
35471
+ shiftDate,
35472
+ timezone,
35473
+ now: now4 = /* @__PURE__ */ new Date()
35474
+ }) => {
35475
+ if (!Array.isArray(data)) {
35476
+ return [];
35477
+ }
35478
+ const normalizedData = data.map((value) => typeof value === "number" && Number.isFinite(value) ? value : null);
35479
+ if (!normalizedData.length) {
35480
+ return normalizedData;
35481
+ }
35482
+ const shiftMinutes = getShiftDurationMinutes(shiftStart, shiftEnd);
35483
+ const elapsedMinutes = getShiftElapsedMinutes({
35484
+ shiftStart,
35485
+ shiftEnd,
35486
+ shiftDate,
35487
+ timezone,
35488
+ now: now4
35489
+ });
35490
+ if (shiftMinutes === null || elapsedMinutes === null || elapsedMinutes >= shiftMinutes) {
35491
+ return normalizedData;
35492
+ }
35493
+ return normalizedData.map((value, index) => {
35494
+ const slotStartMinutes = index * 60;
35495
+ return slotStartMinutes > elapsedMinutes ? null : value;
35496
+ });
35497
+ };
35357
35498
  var buildUptimeSeries = ({
35358
35499
  idleTimeHourly,
35359
35500
  shiftStart,
@@ -44477,7 +44618,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44477
44618
  return null;
44478
44619
  }
44479
44620
  };
44480
- const getShiftIcon = (shift) => {
44621
+ const getShiftIcon2 = (shift) => {
44481
44622
  if (shift === "Day") {
44482
44623
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z", clipRule: "evenodd" }) });
44483
44624
  } else {
@@ -44503,7 +44644,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44503
44644
  }
44504
44645
  if (variant === "enhanced") {
44505
44646
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex items-center gap-2 bg-blue-50 rounded-lg px-3 py-1.5 ${className ?? ""}`, children: [
44506
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon(currentShiftText) }),
44647
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon2(currentShiftText) }),
44507
44648
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-base font-medium text-blue-800", children: [
44508
44649
  currentShiftText,
44509
44650
  " Shift"
@@ -44511,7 +44652,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44511
44652
  ] });
44512
44653
  }
44513
44654
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `inline-flex items-center gap-2 bg-blue-50 rounded-lg px-3 py-1.5 ${className ?? ""}`, children: [
44514
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon(currentShiftText) }),
44655
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon2(currentShiftText) }),
44515
44656
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-base font-medium text-blue-800", children: [
44516
44657
  currentShiftText,
44517
44658
  " Shift"
@@ -47148,7 +47289,7 @@ var LinePdfGenerator = ({
47148
47289
  doc.setLineWidth(0.8);
47149
47290
  doc.line(20, 123, 190, 123);
47150
47291
  const hourlyOverviewStartY = 128;
47151
- const parseTimeToMinutes3 = (timeStr) => {
47292
+ const parseTimeToMinutes4 = (timeStr) => {
47152
47293
  const [hours, minutes] = timeStr.split(":");
47153
47294
  const hour = parseInt(hours, 10);
47154
47295
  const minute = parseInt(minutes || "0", 10);
@@ -47181,7 +47322,7 @@ var LinePdfGenerator = ({
47181
47322
  };
47182
47323
  };
47183
47324
  const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
47184
- const startMinutes = parseTimeToMinutes3(startTimeStr);
47325
+ const startMinutes = parseTimeToMinutes4(startTimeStr);
47185
47326
  if (Number.isNaN(startMinutes)) {
47186
47327
  return [];
47187
47328
  }
@@ -47189,7 +47330,7 @@ var LinePdfGenerator = ({
47189
47330
  const defaultHours = 11;
47190
47331
  return Array.from({ length: defaultHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
47191
47332
  }
47192
- const endMinutes = parseTimeToMinutes3(endTimeStr);
47333
+ const endMinutes = parseTimeToMinutes4(endTimeStr);
47193
47334
  if (Number.isNaN(endMinutes)) {
47194
47335
  const fallbackHours = 11;
47195
47336
  return Array.from({ length: fallbackHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
@@ -48892,7 +49033,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48892
49033
  setIsGenerating(true);
48893
49034
  try {
48894
49035
  const isUptimeMode = workspace.monitoring_mode === "uptime";
48895
- const isAssemblyCycleMode = !isUptimeMode && workspace.line_assembly_enabled === true && workspace.action_type === "assembly";
49036
+ const isAssemblyCycleMode = !isUptimeMode && shouldUseAssemblyCycleTimeLayout(workspace);
48896
49037
  const shiftMinutes = getShiftDurationMinutes(workspace.shift_start, workspace.shift_end);
48897
49038
  const shiftSeconds = shiftMinutes ? shiftMinutes * 60 : 0;
48898
49039
  const idleSeconds = Math.max(workspace.idle_time || 0, 0);
@@ -48958,7 +49099,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48958
49099
  minute: "2-digit",
48959
49100
  hour12: true
48960
49101
  });
48961
- const parseTimeToMinutes3 = (timeValue) => {
49102
+ const parseTimeToMinutes4 = (timeValue) => {
48962
49103
  const [hourPart, minutePart] = timeValue.split(":").map(Number);
48963
49104
  const hour = Number.isFinite(hourPart) ? hourPart : 0;
48964
49105
  const minute = Number.isFinite(minutePart) ? minutePart : 0;
@@ -48975,8 +49116,8 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
48975
49116
  const IST_OFFSET_MINUTES = 330;
48976
49117
  return Date.UTC(year, month - 1, day, hour, minute) - IST_OFFSET_MINUTES * 60 * 1e3;
48977
49118
  };
48978
- const shiftStartMinutes = parseTimeToMinutes3(workspace.shift_start);
48979
- const shiftEndMinutes = parseTimeToMinutes3(workspace.shift_end);
49119
+ const shiftStartMinutes = parseTimeToMinutes4(workspace.shift_start);
49120
+ const shiftEndMinutes = parseTimeToMinutes4(workspace.shift_end);
48980
49121
  const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
48981
49122
  const shiftStartUtcMs = toShiftUtcMs(workspace.date, workspace.shift_start);
48982
49123
  const shiftEndUtcMs = toShiftUtcMs(workspace.date, workspace.shift_end) + (wrapsMidnight ? 24 * 60 * 60 * 1e3 : 0);
@@ -51283,7 +51424,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51283
51424
  const rawName = currentShift.shiftName || "Day";
51284
51425
  return rawName.toLowerCase().includes("shift") ? rawName : `${rawName} Shift`;
51285
51426
  };
51286
- const getShiftIcon = () => {
51427
+ const getShiftIcon2 = () => {
51287
51428
  const currentShift = getCurrentShift(timezone, shiftConfig);
51288
51429
  const shiftName = (currentShift.shiftName || "").toLowerCase();
51289
51430
  if (shiftName.includes("day") || shiftName.includes("morning") || currentShift.shiftId === 0) {
@@ -51317,7 +51458,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51317
51458
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: /* @__PURE__ */ jsxRuntime.jsx(Timer2, {}) }),
51318
51459
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-300", children: "|" }),
51319
51460
  isShiftConfigLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-16 bg-gray-200 rounded animate-pulse" }) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 font-medium", children: [
51320
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75", children: getShiftIcon() }),
51461
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75", children: getShiftIcon2() }),
51321
51462
  getShiftName()
51322
51463
  ] })
51323
51464
  ] })
@@ -51334,7 +51475,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51334
51475
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 inline-flex flex-wrap items-center gap-3", children: [
51335
51476
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs md:text-sm font-medium text-gray-600 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx(Timer2, {}) }),
51336
51477
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center gap-1", children: isShiftConfigLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-20 bg-gray-200 rounded animate-pulse" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
51337
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon() }),
51478
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon2() }),
51338
51479
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs md:text-sm font-medium text-gray-600 whitespace-nowrap", children: getShiftName() })
51339
51480
  ] }) })
51340
51481
  ] })
@@ -58305,7 +58446,7 @@ var FactoryView = ({
58305
58446
  const currentShift = getCurrentShiftInfo();
58306
58447
  return (currentShift.shiftName || "Day").replace(/ Shift$/i, "");
58307
58448
  };
58308
- const getShiftIcon = () => {
58449
+ const getShiftIcon2 = () => {
58309
58450
  const currentShift = getCurrentShiftInfo();
58310
58451
  const shiftNameLower = (currentShift.shiftName || "").toLowerCase();
58311
58452
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || currentShift.shiftId === 0) {
@@ -58351,7 +58492,7 @@ var FactoryView = ({
58351
58492
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center justify-center gap-2", children: [
58352
58493
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-gray-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) }) }),
58353
58494
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
58354
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon() }),
58495
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2() }),
58355
58496
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-gray-700", children: [
58356
58497
  getShiftName(),
58357
58498
  " Shift"
@@ -58370,7 +58511,7 @@ var FactoryView = ({
58370
58511
  " IST"
58371
58512
  ] }),
58372
58513
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1", children: [
58373
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon() }),
58514
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon2() }),
58374
58515
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
58375
58516
  getShiftName(),
58376
58517
  " Shift"
@@ -61082,7 +61223,7 @@ var KPIDetailView = ({
61082
61223
  const getShiftName = React141.useCallback((shiftId) => {
61083
61224
  return getShiftNameById(shiftId, configuredTimezone, shiftConfig);
61084
61225
  }, [configuredTimezone, shiftConfig]);
61085
- const getShiftIcon = React141.useCallback((shiftId) => {
61226
+ const getShiftIcon2 = React141.useCallback((shiftId) => {
61086
61227
  if (shiftId === 0) {
61087
61228
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
61088
61229
  }
@@ -61962,7 +62103,7 @@ var KPIDetailView = ({
61962
62103
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
61963
62104
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-gray-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: chartMetrics && formatLocalDate(new Date(chartMetrics.date)) }) }),
61964
62105
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
61965
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(chartMetrics.shift_id ?? 0) }),
62106
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2(chartMetrics.shift_id ?? 0) }),
61966
62107
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-gray-700", children: [
61967
62108
  getShiftName(chartMetrics.shift_id ?? 0).replace(/ Shift$/i, ""),
61968
62109
  " Shift"
@@ -61988,7 +62129,7 @@ var KPIDetailView = ({
61988
62129
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
61989
62130
  ] }),
61990
62131
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
61991
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(chartMetrics.shift_id ?? 0) }),
62132
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(chartMetrics.shift_id ?? 0) }),
61992
62133
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
61993
62134
  getShiftName(chartMetrics.shift_id ?? 0).replace(/ Shift$/i, ""),
61994
62135
  " Shift"
@@ -62006,7 +62147,7 @@ var KPIDetailView = ({
62006
62147
  return `${startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
62007
62148
  })() }) }),
62008
62149
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
62009
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(selectedShiftId) }),
62150
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2(selectedShiftId) }),
62010
62151
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: getShiftName(selectedShiftId) })
62011
62152
  ] })
62012
62153
  ] }),
@@ -62023,7 +62164,7 @@ var KPIDetailView = ({
62023
62164
  ] }),
62024
62165
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
62025
62166
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
62026
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(selectedShiftId) }),
62167
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(selectedShiftId) }),
62027
62168
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
62028
62169
  getShiftName(selectedShiftId).replace(/ Shift$/i, ""),
62029
62170
  " Shift"
@@ -63399,7 +63540,7 @@ var KPIsOverviewView = ({
63399
63540
  const headerShiftId = showHistoricalLeaderboardHeader ? effectiveLeaderboardShiftId : currentShiftDetails.shiftId;
63400
63541
  const headerShiftName = getShiftNameById(headerShiftId, configuredTimezone, shiftConfig).replace(/ Shift$/i, "");
63401
63542
  const headerDateLabel = isMonthlyMode ? getMonthRange() : formatLocalDate2(headerDateKey);
63402
- const getShiftIcon = (shiftId) => {
63543
+ const getShiftIcon2 = (shiftId) => {
63403
63544
  const shiftNameLower = getShiftNameById(shiftId, configuredTimezone, shiftConfig).toLowerCase();
63404
63545
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || shiftId === 0) {
63405
63546
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
@@ -63516,7 +63657,7 @@ var KPIsOverviewView = ({
63516
63657
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `inline-flex items-center bg-gray-100 rounded-full ${isMonthlyMode ? "px-4 py-1.5 ring-1 ring-gray-200 shadow-sm" : "px-2.5 py-1"}`, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `font-medium text-gray-700 ${isMonthlyMode ? "text-sm" : "text-xs"}`, children: headerDateLabel }) }),
63517
63658
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63518
63659
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
63519
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(headerShiftId) }),
63660
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2(headerShiftId) }),
63520
63661
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: headerShiftName })
63521
63662
  ] }),
63522
63663
  showHistoricalLeaderboardHeader ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-amber-50 text-amber-700 rounded-full", children: [
@@ -63712,7 +63853,7 @@ var KPIsOverviewView = ({
63712
63853
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63713
63854
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
63714
63855
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
63715
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(headerShiftId) }),
63856
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(headerShiftId) }),
63716
63857
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
63717
63858
  headerShiftName,
63718
63859
  " Shift"
@@ -63856,6 +63997,52 @@ var KPIsOverviewView = ({
63856
63997
  ] });
63857
63998
  };
63858
63999
  var KPIsOverviewView_default = KPIsOverviewView;
64000
+ var toFiniteNumber = (value) => {
64001
+ if (typeof value === "number" && Number.isFinite(value)) return value;
64002
+ if (typeof value === "string" && value.trim() !== "") {
64003
+ const parsed = Number(value);
64004
+ return Number.isFinite(parsed) ? parsed : null;
64005
+ }
64006
+ return null;
64007
+ };
64008
+ var getCycleRatio = (workspace) => {
64009
+ const idealCycleTime = toFiniteNumber(workspace.ideal_cycle_time);
64010
+ const avgCycleTime = toFiniteNumber(workspace.avg_cycle_time);
64011
+ if (idealCycleTime === null || avgCycleTime === null || idealCycleTime <= 0 || avgCycleTime <= 0) {
64012
+ return null;
64013
+ }
64014
+ return idealCycleTime / avgCycleTime;
64015
+ };
64016
+ var formatCycleTimeValue = (value) => {
64017
+ const numericValue = toFiniteNumber(value);
64018
+ if (numericValue === null || numericValue <= 0) return "--";
64019
+ return `${numericValue.toFixed(1)}s`;
64020
+ };
64021
+ var CycleTimeComparison = React141.memo(({
64022
+ workspace,
64023
+ variant = "table"
64024
+ }) => {
64025
+ const averageValue = formatCycleTimeValue(workspace.avg_cycle_time);
64026
+ const standardValue = formatCycleTimeValue(workspace.ideal_cycle_time);
64027
+ if (variant === "mobile") {
64028
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center justify-between py-2 border-t border-gray-100", children: [
64029
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Standard Cycle Time" }),
64030
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium tabular-nums text-gray-500", children: standardValue })
64031
+ ] });
64032
+ }
64033
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
64034
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
64035
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wider text-gray-500", children: "Average" }),
64036
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tabular-nums text-gray-900", children: averageValue })
64037
+ ] }),
64038
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-px bg-gray-200" }),
64039
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
64040
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wider text-gray-500", children: "Standard" }),
64041
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium tabular-nums text-gray-500", children: standardValue })
64042
+ ] })
64043
+ ] });
64044
+ }, (prevProps, nextProps) => prevProps.variant === nextProps.variant && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.ideal_cycle_time === nextProps.workspace.ideal_cycle_time);
64045
+ CycleTimeComparison.displayName = "CycleTimeComparison";
63859
64046
  var IsolatedTimer = React141.memo(() => {
63860
64047
  return /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {});
63861
64048
  });
@@ -63873,11 +64060,11 @@ var HeaderRibbon = React141.memo(({
63873
64060
  currentDate,
63874
64061
  currentMobileDate,
63875
64062
  shiftId,
63876
- getShiftIcon,
64063
+ getShiftIcon: getShiftIcon2,
63877
64064
  getShiftName,
63878
64065
  showTimer = true
63879
64066
  }) => {
63880
- const shiftIcon = React141.useMemo(() => getShiftIcon(shiftId), [getShiftIcon, shiftId]);
64067
+ const shiftIcon = React141.useMemo(() => getShiftIcon2(shiftId), [getShiftIcon2, shiftId]);
63881
64068
  const shiftName = React141.useMemo(() => getShiftName(shiftId), [getShiftName, shiftId]);
63882
64069
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63883
64070
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
@@ -63919,8 +64106,9 @@ var MobileWorkspaceCard = React141.memo(({
63919
64106
  isClickable,
63920
64107
  onWorkspaceClick,
63921
64108
  getMedalIcon,
63922
- efficiencyLabel
63923
- }) => /* @__PURE__ */ jsxRuntime.jsx(
64109
+ metricLabel,
64110
+ isAssemblyMode
64111
+ }) => /* @__PURE__ */ jsxRuntime.jsxs(
63924
64112
  motion.div,
63925
64113
  {
63926
64114
  layout: true,
@@ -63931,28 +64119,31 @@ var MobileWorkspaceCard = React141.memo(({
63931
64119
  },
63932
64120
  onClick: isClickable ? () => onWorkspaceClick(workspace, rank) : void 0,
63933
64121
  className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] ${isClickable ? "cursor-pointer" : "cursor-not-allowed opacity-75"}`,
63934
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
63935
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
63936
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
63937
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-700", children: [
63938
- "#",
63939
- rank
64122
+ children: [
64123
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2 gap-3", children: [
64124
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
64125
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
64126
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-700", children: [
64127
+ "#",
64128
+ rank
64129
+ ] }),
64130
+ getMedalIcon(rank)
63940
64131
  ] }),
63941
- getMedalIcon(rank)
64132
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
64133
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children: workspace.displayName }),
64134
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: workspace.lineName })
64135
+ ] })
63942
64136
  ] }),
63943
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
63944
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children: workspace.displayName }),
63945
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: workspace.lineName })
64137
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
64138
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-bold text-gray-900 text-lg", children: isAssemblyMode ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: formatCycleTimeValue(workspace.avg_cycle_time) }) : /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: workspace.efficiency || 0 }) }),
64139
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: metricLabel })
63946
64140
  ] })
63947
64141
  ] }),
63948
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
63949
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-lg font-bold text-gray-900", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: workspace.efficiency || 0 }) }),
63950
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: efficiencyLabel })
63951
- ] })
63952
- ] })
64142
+ isAssemblyMode && /* @__PURE__ */ jsxRuntime.jsx(CycleTimeComparison, { workspace, variant: "mobile" })
64143
+ ]
63953
64144
  }
63954
64145
  ), (prevProps, nextProps) => {
63955
- return prevProps.efficiencyLabel === nextProps.efficiencyLabel && prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.isClickable === nextProps.isClickable && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
64146
+ return prevProps.metricLabel === nextProps.metricLabel && prevProps.isAssemblyMode === nextProps.isAssemblyMode && prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.isClickable === nextProps.isClickable && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.ideal_cycle_time === nextProps.workspace.ideal_cycle_time && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
63956
64147
  });
63957
64148
  MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
63958
64149
  var DesktopWorkspaceRow = React141.memo(({
@@ -63961,7 +64152,8 @@ var DesktopWorkspaceRow = React141.memo(({
63961
64152
  rowClass,
63962
64153
  isClickable,
63963
64154
  onWorkspaceClick,
63964
- getMedalIcon
64155
+ getMedalIcon,
64156
+ isAssemblyMode
63965
64157
  }) => /* @__PURE__ */ jsxRuntime.jsxs(
63966
64158
  motion.tr,
63967
64159
  {
@@ -63978,11 +64170,11 @@ var DesktopWorkspaceRow = React141.memo(({
63978
64170
  ] }) }),
63979
64171
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.displayName }) }),
63980
64172
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.lineName }) }),
63981
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: workspace.efficiency || 0 }) })
64173
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: `px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium ${isAssemblyMode ? "" : "whitespace-nowrap"}`, children: isAssemblyMode ? /* @__PURE__ */ jsxRuntime.jsx(CycleTimeComparison, { workspace, variant: "table" }) : /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: workspace.efficiency || 0 }) })
63982
64174
  ]
63983
64175
  }
63984
64176
  ), (prevProps, nextProps) => {
63985
- return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.isClickable === nextProps.isClickable && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
64177
+ return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.isClickable === nextProps.isClickable && prevProps.isAssemblyMode === nextProps.isAssemblyMode && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.ideal_cycle_time === nextProps.workspace.ideal_cycle_time && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName;
63986
64178
  });
63987
64179
  DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
63988
64180
  var LeaderboardDetailView = React141.memo(({
@@ -64004,6 +64196,7 @@ var LeaderboardDetailView = React141.memo(({
64004
64196
  const supabase = useSupabase();
64005
64197
  const [sortAscending, setSortAscending] = React141.useState(false);
64006
64198
  const [viewType, setViewType] = React141.useState("operator");
64199
+ const [outputCategory, setOutputCategory] = React141.useState("standard");
64007
64200
  const [activeTab, setActiveTab] = React141.useState("today");
64008
64201
  const timezone = useAppTimezone();
64009
64202
  const staticShiftConfig = useShiftConfig();
@@ -64082,6 +64275,7 @@ var LeaderboardDetailView = React141.memo(({
64082
64275
  const monthlyRequestKeyRef = React141.useRef(null);
64083
64276
  const leaderboardUpdateQueuedRef = React141.useRef(false);
64084
64277
  const leaderboardUpdateTimerRef = React141.useRef(null);
64278
+ const leaderboardViewTrackedRef = React141.useRef(null);
64085
64279
  const filterRef = React141.useRef(null);
64086
64280
  const filterButtonRef = React141.useRef(null);
64087
64281
  const mobileFilterButtonRef = React141.useRef(null);
@@ -64214,6 +64408,17 @@ var LeaderboardDetailView = React141.memo(({
64214
64408
  }
64215
64409
  return Array.from(uniqueLines.entries()).map(([id3, name]) => ({ id: id3, label: name }));
64216
64410
  }, [configuredLineIds, lines, getLineName]);
64411
+ const scopedLines = React141.useMemo(() => {
64412
+ const configuredLineIdSet = new Set(configuredLineIds);
64413
+ return lines.filter((line) => configuredLineIds.length === 0 || configuredLineIdSet.has(line.id));
64414
+ }, [configuredLineIds, lines]);
64415
+ const scopedLineAssemblyMap = React141.useMemo(() => {
64416
+ const map = /* @__PURE__ */ new Map();
64417
+ scopedLines.forEach((line) => {
64418
+ map.set(line.id, line.assembly === true);
64419
+ });
64420
+ return map;
64421
+ }, [scopedLines]);
64217
64422
  const shiftOptions = React141.useMemo(() => {
64218
64423
  if (activeShiftConfig?.shifts && activeShiftConfig.shifts.length > 0) {
64219
64424
  return activeShiftConfig.shifts.map((shift2) => ({
@@ -64237,6 +64442,33 @@ var LeaderboardDetailView = React141.memo(({
64237
64442
  const parsed = Number(selectedShiftFilter);
64238
64443
  return Number.isFinite(parsed) ? parsed : void 0;
64239
64444
  }, [selectedShiftFilter]);
64445
+ const outputScopeLines = React141.useMemo(() => {
64446
+ const outputLines = scopedLines.filter((line) => (line.monitoring_mode ?? "output") !== "uptime");
64447
+ if (selectedLineFilter === "all") {
64448
+ return outputLines;
64449
+ }
64450
+ return outputLines.filter((line) => line.id === selectedLineFilter);
64451
+ }, [scopedLines, selectedLineFilter]);
64452
+ const hasAssemblyOutputScope = React141.useMemo(
64453
+ () => outputScopeLines.some((line) => line.assembly === true),
64454
+ [outputScopeLines]
64455
+ );
64456
+ const hasStandardOutputScope = React141.useMemo(
64457
+ () => outputScopeLines.some((line) => line.assembly !== true),
64458
+ [outputScopeLines]
64459
+ );
64460
+ const showOutputCategoryDropdown = viewType === "operator" && hasAssemblyOutputScope && hasStandardOutputScope;
64461
+ const isAssemblyMode = viewType === "operator" && outputCategory === "assembly";
64462
+ React141.useEffect(() => {
64463
+ if (viewType !== "operator") return;
64464
+ if (hasAssemblyOutputScope && !hasStandardOutputScope && outputCategory !== "assembly") {
64465
+ setOutputCategory("assembly");
64466
+ return;
64467
+ }
64468
+ if (hasStandardOutputScope && !hasAssemblyOutputScope && outputCategory !== "standard") {
64469
+ setOutputCategory("standard");
64470
+ }
64471
+ }, [viewType, hasAssemblyOutputScope, hasStandardOutputScope, outputCategory]);
64240
64472
  const handleLineFilterChange = React141.useCallback((value) => {
64241
64473
  setSelectedLineFilter(value);
64242
64474
  }, []);
@@ -64246,17 +64478,19 @@ var LeaderboardDetailView = React141.memo(({
64246
64478
  const clearFilters = React141.useCallback(() => {
64247
64479
  setSortAscending(false);
64248
64480
  setSelectedLineFilter("all");
64481
+ setOutputCategory("standard");
64249
64482
  setSelectedShiftFilter(currentShiftInfo.shiftId.toString());
64250
64483
  }, [currentShiftInfo.shiftId]);
64251
64484
  const activeFiltersCount = React141.useMemo(() => {
64252
64485
  let count = 0;
64253
64486
  if (sortAscending) count++;
64254
64487
  if (selectedLineFilter !== "all") count++;
64488
+ if (showOutputCategoryDropdown && outputCategory !== "standard") count++;
64255
64489
  if (selectedShiftFilter !== currentShiftInfo.shiftId.toString()) {
64256
64490
  count++;
64257
64491
  }
64258
64492
  return count;
64259
- }, [sortAscending, selectedLineFilter, selectedShiftFilter, currentShiftInfo.shiftId]);
64493
+ }, [sortAscending, selectedLineFilter, outputCategory, showOutputCategoryDropdown, selectedShiftFilter, currentShiftInfo.shiftId]);
64260
64494
  const shouldFetchShiftConfigs = !date && shiftId === void 0;
64261
64495
  const {
64262
64496
  shiftConfigMap: multiLineShiftConfigMap,
@@ -64308,7 +64542,8 @@ var LeaderboardDetailView = React141.memo(({
64308
64542
  action_count: entry.total_output || 0,
64309
64543
  pph: entry.avg_pph || 0,
64310
64544
  performance_score: entry.performance_score ?? 0,
64311
- avg_cycle_time: entry.avg_cycle_time || 0,
64545
+ avg_cycle_time: toFiniteNumber(entry.avg_cycle_time) ?? 0,
64546
+ ideal_cycle_time: toFiniteNumber(entry.ideal_cycle_time) ?? void 0,
64312
64547
  trend: 0,
64313
64548
  predicted_output: 0,
64314
64549
  efficiency: entry.efficiency || 0,
@@ -64486,7 +64721,7 @@ var LeaderboardDetailView = React141.memo(({
64486
64721
  const currentShift = getCurrentShift(timezone || "Asia/Kolkata", activeShiftConfig);
64487
64722
  return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", activeShiftConfig);
64488
64723
  }, [timezone, activeShiftConfig, shiftGroups]);
64489
- const getShiftIcon = React141.useCallback((shiftId2) => {
64724
+ const getShiftIcon2 = React141.useCallback((shiftId2) => {
64490
64725
  if (shiftId2 === void 0 && shiftGroups.length > 1) {
64491
64726
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
64492
64727
  }
@@ -64552,15 +64787,20 @@ var LeaderboardDetailView = React141.memo(({
64552
64787
  if (!canOpenWorkspace(workspace.line_id)) {
64553
64788
  return;
64554
64789
  }
64790
+ const cycleRatio = getCycleRatio(workspace);
64555
64791
  trackCoreEvent("Workspace from Leaderboard Clicked", {
64556
64792
  workspace_name: workspace.workspace_name,
64557
64793
  workspace_id: workspace.workspace_uuid,
64558
64794
  rank,
64559
64795
  total_workspaces: workspacesLengthRef.current,
64560
64796
  // Use ref instead of state to avoid dependency
64797
+ metric_context: viewType === "machine" ? "machine" : outputCategory,
64561
64798
  efficiency: workspace.efficiency,
64562
64799
  action_count: workspace.action_count,
64563
- action_threshold: workspace.action_threshold
64800
+ action_threshold: workspace.action_threshold,
64801
+ avg_cycle_time: workspace.avg_cycle_time,
64802
+ ideal_cycle_time: workspace.ideal_cycle_time ?? null,
64803
+ cycle_ratio: cycleRatio
64564
64804
  });
64565
64805
  const displayName = workspace.displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
64566
64806
  const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName, workspace.line_id) : "";
@@ -64584,7 +64824,7 @@ var LeaderboardDetailView = React141.memo(({
64584
64824
  const combinedParams = navParams ? `${navParams}&${contextParamString}` : `?${contextParamString}`;
64585
64825
  navigation.navigate(`/workspace/${workspace.workspace_uuid}${combinedParams}`);
64586
64826
  }
64587
- }, [canOpenWorkspace, onWorkspaceClick, navigation, date, shiftId]);
64827
+ }, [canOpenWorkspace, onWorkspaceClick, navigation, date, shiftId, outputCategory, viewType]);
64588
64828
  React141.useEffect(() => {
64589
64829
  workspacesLengthRef.current = activeEntries.length || 0;
64590
64830
  }, [activeEntries.length]);
@@ -64605,16 +64845,20 @@ var LeaderboardDetailView = React141.memo(({
64605
64845
  return activeEntries.map((ws) => ({
64606
64846
  ...ws,
64607
64847
  displayName: ws.displayName || getWorkspaceDisplayName(ws.workspace_name, ws.line_id),
64608
- lineName: getLineName(ws.line_id)
64848
+ lineName: getLineName(ws.line_id),
64849
+ isAssemblyLine: scopedLineAssemblyMap.get(ws.line_id) === true
64609
64850
  }));
64610
- }, [activeEntries, getLineName]);
64851
+ }, [activeEntries, getLineName, scopedLineAssemblyMap]);
64611
64852
  const sortedWorkspaces = React141.useMemo(() => {
64612
64853
  let filtered = [...workspaceDisplayData];
64613
64854
  filtered = filtered.filter((ws) => {
64614
64855
  if (viewType === "machine") {
64615
64856
  return ws.monitoring_mode === "uptime";
64616
64857
  }
64617
- return ws.monitoring_mode !== "uptime";
64858
+ if (ws.monitoring_mode === "uptime") {
64859
+ return false;
64860
+ }
64861
+ return isAssemblyMode ? ws.isAssemblyLine : !ws.isAssemblyLine;
64618
64862
  });
64619
64863
  if (selectedLineFilter !== "all") {
64620
64864
  filtered = filtered.filter((ws) => ws.line_id === selectedLineFilter);
@@ -64623,13 +64867,61 @@ var LeaderboardDetailView = React141.memo(({
64623
64867
  filtered = filtered.filter((ws) => ws.shift_id?.toString() === selectedShiftFilter);
64624
64868
  }
64625
64869
  return filtered.sort((a, b) => {
64870
+ if (isAssemblyMode) {
64871
+ const ratioA = getCycleRatio(a);
64872
+ const ratioB = getCycleRatio(b);
64873
+ if (ratioA === null && ratioB === null) return 0;
64874
+ if (ratioA === null) return 1;
64875
+ if (ratioB === null) return -1;
64876
+ return sortAscending ? ratioA - ratioB : ratioB - ratioA;
64877
+ }
64626
64878
  const effA = a.efficiency || 0;
64627
64879
  const effB = b.efficiency || 0;
64628
64880
  return sortAscending ? effA - effB : effB - effA;
64629
64881
  });
64630
- }, [workspaceDisplayData, sortAscending, selectedLineFilter, selectedShiftFilter, activeTab, viewType]);
64882
+ }, [workspaceDisplayData, sortAscending, selectedLineFilter, selectedShiftFilter, activeTab, viewType, isAssemblyMode]);
64631
64883
  const loading = activeTab === "today" ? todayLoading : monthlyLoading;
64632
64884
  const error = activeTab === "today" ? todayError : monthlyError;
64885
+ React141.useEffect(() => {
64886
+ if (loading || error || sortedWorkspaces.length === 0) return;
64887
+ const trackingKey = [
64888
+ activeTab,
64889
+ viewType,
64890
+ outputCategory,
64891
+ selectedLineFilter,
64892
+ selectedShiftFilter,
64893
+ sortAscending ? "asc" : "desc",
64894
+ sortedWorkspaces.length
64895
+ ].join("|");
64896
+ if (leaderboardViewTrackedRef.current === trackingKey) return;
64897
+ leaderboardViewTrackedRef.current = trackingKey;
64898
+ const topWorkspace = sortedWorkspaces[0];
64899
+ trackCoreEvent("Workspace Leaderboard View Loaded", {
64900
+ time_range: activeTab,
64901
+ view_type: viewType,
64902
+ metric_context: viewType === "machine" ? "machine" : outputCategory,
64903
+ line_filter: selectedLineFilter,
64904
+ shift_filter: selectedShiftFilter,
64905
+ sort_direction: sortAscending ? "asc" : "desc",
64906
+ workspace_count: sortedWorkspaces.length,
64907
+ top_workspace_id: topWorkspace?.workspace_uuid ?? null,
64908
+ top_workspace_name: topWorkspace?.workspace_name ?? null,
64909
+ top_efficiency: topWorkspace?.efficiency ?? null,
64910
+ top_avg_cycle_time: topWorkspace?.avg_cycle_time ?? null,
64911
+ top_ideal_cycle_time: topWorkspace?.ideal_cycle_time ?? null,
64912
+ top_cycle_ratio: topWorkspace ? getCycleRatio(topWorkspace) : null
64913
+ });
64914
+ }, [
64915
+ loading,
64916
+ error,
64917
+ sortedWorkspaces,
64918
+ activeTab,
64919
+ viewType,
64920
+ outputCategory,
64921
+ selectedLineFilter,
64922
+ selectedShiftFilter,
64923
+ sortAscending
64924
+ ]);
64633
64925
  const currentDateFormatted = React141.useMemo(() => {
64634
64926
  const dateStr = (/* @__PURE__ */ new Date()).toDateString();
64635
64927
  return formatDate2(new Date(dateStr));
@@ -64647,7 +64939,9 @@ var LeaderboardDetailView = React141.memo(({
64647
64939
  error.message
64648
64940
  ] }) });
64649
64941
  }
64650
- const efficiencyLabel = viewType === "machine" ? "Utilization" : "Efficiency";
64942
+ const metricLabel = viewType === "machine" ? "Utilization" : isAssemblyMode ? "Cycle Time" : "Efficiency";
64943
+ const descendingSortLabel = "Highest to Lowest";
64944
+ const ascendingSortLabel = "Lowest to Highest";
64651
64945
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, style: { willChange: "contents" }, children: [
64652
64946
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-20 bg-white shadow-sm border-b border-gray-200/80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-6 md:px-8 py-2 sm:py-2.5", children: [
64653
64947
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
@@ -64697,7 +64991,7 @@ var LeaderboardDetailView = React141.memo(({
64697
64991
  ] }),
64698
64992
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
64699
64993
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
64700
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: efficiencyLabel }),
64994
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: metricLabel }),
64701
64995
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
64702
64996
  "select",
64703
64997
  {
@@ -64706,8 +65000,25 @@ var LeaderboardDetailView = React141.memo(({
64706
65000
  className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
64707
65001
  style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.75rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
64708
65002
  children: [
64709
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "desc", children: "Highest to Lowest" }),
64710
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "asc", children: "Lowest to Highest" })
65003
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "desc", children: descendingSortLabel }),
65004
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "asc", children: ascendingSortLabel })
65005
+ ]
65006
+ }
65007
+ ) })
65008
+ ] }),
65009
+ showOutputCategoryDropdown && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 sm:hidden", children: [
65010
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Output Type" }),
65011
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
65012
+ "select",
65013
+ {
65014
+ "aria-label": "Output leaderboard category",
65015
+ value: outputCategory,
65016
+ onChange: (e) => setOutputCategory(e.target.value),
65017
+ className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
65018
+ style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.75rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
65019
+ children: [
65020
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "standard", children: "Standard" }),
65021
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", children: "Assembly" })
64711
65022
  ]
64712
65023
  }
64713
65024
  ) })
@@ -64749,7 +65060,7 @@ var LeaderboardDetailView = React141.memo(({
64749
65060
  currentDate: activeTab === "monthly" ? monthlyRangeText : currentDateFormatted,
64750
65061
  currentMobileDate: activeTab === "monthly" ? monthlyRangeText : currentMobileDateFormatted,
64751
65062
  shiftId: activeTab === "monthly" ? monthlyShiftId : todayShiftId,
64752
- getShiftIcon,
65063
+ getShiftIcon: getShiftIcon2,
64753
65064
  getShiftName,
64754
65065
  showTimer: activeTab === "today"
64755
65066
  }
@@ -64847,6 +65158,20 @@ var LeaderboardDetailView = React141.memo(({
64847
65158
  ]
64848
65159
  }
64849
65160
  ) }),
65161
+ showOutputCategoryDropdown && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
65162
+ "select",
65163
+ {
65164
+ "aria-label": "Output leaderboard category",
65165
+ value: outputCategory,
65166
+ onChange: (e) => setOutputCategory(e.target.value),
65167
+ className: "appearance-none pl-3 pr-8 py-1.5 text-sm font-medium bg-white border border-gray-200 hover:border-gray-300 rounded-lg text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer shadow-sm",
65168
+ style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.5rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
65169
+ children: [
65170
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "standard", children: "Standard" }),
65171
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", children: "Assembly" })
65172
+ ]
65173
+ }
65174
+ ) }),
64850
65175
  /* @__PURE__ */ jsxRuntime.jsxs(
64851
65176
  "button",
64852
65177
  {
@@ -64878,7 +65203,8 @@ var LeaderboardDetailView = React141.memo(({
64878
65203
  isClickable: canOpenWorkspace(ws.line_id),
64879
65204
  onWorkspaceClick: stableHandleWorkspaceClick,
64880
65205
  getMedalIcon: stableGetMedalIcon,
64881
- efficiencyLabel
65206
+ metricLabel,
65207
+ isAssemblyMode
64882
65208
  },
64883
65209
  ws.workspace_uuid
64884
65210
  );
@@ -64889,7 +65215,7 @@ var LeaderboardDetailView = React141.memo(({
64889
65215
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Rank" }),
64890
65216
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Workspace" }),
64891
65217
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Line" }),
64892
- /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: efficiencyLabel })
65218
+ /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: metricLabel })
64893
65219
  ] }) }),
64894
65220
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-100", children: sortedWorkspaces.map((ws, index) => {
64895
65221
  const isTopThree = index < 3;
@@ -64902,7 +65228,8 @@ var LeaderboardDetailView = React141.memo(({
64902
65228
  rowClass,
64903
65229
  isClickable: canOpenWorkspace(ws.line_id),
64904
65230
  onWorkspaceClick: stableHandleWorkspaceClick,
64905
- getMedalIcon: stableGetMedalIcon
65231
+ getMedalIcon: stableGetMedalIcon,
65232
+ isAssemblyMode
64906
65233
  },
64907
65234
  ws.workspace_uuid
64908
65235
  );
@@ -64920,7 +65247,10 @@ function LoginView({
64920
65247
  logoSrc = optifye_logo_default,
64921
65248
  logoAlt = "Optifye",
64922
65249
  brandName = "Optifye",
64923
- onRateLimitCheck
65250
+ onRateLimitCheck,
65251
+ showDevTestLogin = false,
65252
+ devTestLoginLabel,
65253
+ onDevTestLogin
64924
65254
  }) {
64925
65255
  return /* @__PURE__ */ jsxRuntime.jsx(
64926
65256
  LoginPage,
@@ -64928,7 +65258,10 @@ function LoginView({
64928
65258
  logoSrc,
64929
65259
  logoAlt,
64930
65260
  brandName,
64931
- onRateLimitCheck
65261
+ onRateLimitCheck,
65262
+ showDevTestLogin,
65263
+ devTestLoginLabel,
65264
+ onDevTestLogin
64932
65265
  }
64933
65266
  );
64934
65267
  }
@@ -68063,8 +68396,84 @@ var TargetsView = ({
68063
68396
  var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
68064
68397
  var TargetsView_default = TargetsViewWithDisplayNames;
68065
68398
  var AuthenticatedTargetsView = withAuth(React141__namespace.default.memo(TargetsViewWithDisplayNames));
68399
+ function useTimezone(options = {}) {
68400
+ const dashboardConfig = useDashboardConfig();
68401
+ const workspaceConfig = useWorkspaceConfig();
68402
+ const defaultTimezone = dashboardConfig?.dateTimeConfig?.defaultTimezone || "Asia/Kolkata";
68403
+ const [timezone, setTimezone] = React141.useState(defaultTimezone);
68404
+ const [isLoading, setIsLoading] = React141.useState(true);
68405
+ const [error, setError] = React141.useState(null);
68406
+ const fetchTimezone = React141.useCallback(async () => {
68407
+ setIsLoading(true);
68408
+ setError(null);
68409
+ try {
68410
+ let fetchedTimezone = defaultTimezone;
68411
+ if (options.lineId) {
68412
+ fetchedTimezone = await timezoneService.getTimezoneForLine(options.lineId, defaultTimezone);
68413
+ } else if (options.workspaceId || workspaceConfig && "id" in workspaceConfig) {
68414
+ const wsId = options.workspaceId || (workspaceConfig && "id" in workspaceConfig ? workspaceConfig.id : void 0);
68415
+ if (wsId) {
68416
+ fetchedTimezone = await timezoneService.getTimezoneForWorkspace(wsId, defaultTimezone);
68417
+ }
68418
+ } else if (options.companyId || dashboardConfig && "company" in dashboardConfig) {
68419
+ const compId = options.companyId || (dashboardConfig && "company" in dashboardConfig ? dashboardConfig.company?.id : void 0);
68420
+ if (compId) {
68421
+ fetchedTimezone = await timezoneService.getTimezoneForCompany(compId, defaultTimezone);
68422
+ }
68423
+ }
68424
+ setTimezone(fetchedTimezone);
68425
+ } catch (err) {
68426
+ console.error("Error fetching timezone:", err);
68427
+ setError(err instanceof Error ? err : new Error("Failed to fetch timezone"));
68428
+ setTimezone(defaultTimezone);
68429
+ } finally {
68430
+ setIsLoading(false);
68431
+ }
68432
+ }, [
68433
+ options.lineId,
68434
+ options.workspaceId,
68435
+ options.companyId,
68436
+ workspaceConfig,
68437
+ dashboardConfig,
68438
+ defaultTimezone
68439
+ ]);
68440
+ React141.useEffect(() => {
68441
+ fetchTimezone();
68442
+ }, [fetchTimezone]);
68443
+ return {
68444
+ timezone,
68445
+ isLoading,
68446
+ error,
68447
+ refetch: fetchTimezone
68448
+ };
68449
+ }
68066
68450
 
68067
68451
  // src/views/workspace-detail-view.utils.ts
68452
+ var getWorkspaceDetailLayoutMode = ({
68453
+ workspace,
68454
+ showCycleTimeChart
68455
+ }) => {
68456
+ if (workspace?.monitoring_mode === "uptime") {
68457
+ return "uptime";
68458
+ }
68459
+ if (showCycleTimeChart === false) {
68460
+ return "output";
68461
+ }
68462
+ return shouldUseAssemblyCycleTimeLayout(workspace) ? "assembly_cycle" : "output";
68463
+ };
68464
+ var getCycleTimeRenderState = ({
68465
+ workspace,
68466
+ authoritativeMetrics,
68467
+ showCycleTimeChart
68468
+ }) => {
68469
+ if (getWorkspaceDetailLayoutMode({ workspace, showCycleTimeChart }) !== "assembly_cycle") {
68470
+ return "output";
68471
+ }
68472
+ if (!authoritativeMetrics) {
68473
+ return "chart_loading";
68474
+ }
68475
+ return authoritativeMetrics.cycle_time_data_status === "missing_clips" ? "cycle_unavailable" : "cycle_chart";
68476
+ };
68068
68477
  var formatDateInTimezone = (date = /* @__PURE__ */ new Date(), timezone, options) => {
68069
68478
  const defaultOptions = {
68070
68479
  day: "numeric",
@@ -68448,6 +68857,7 @@ var WorkspaceDetailView = ({
68448
68857
  date: cachedOverviewMetrics.date,
68449
68858
  shift_id: cachedOverviewMetrics.shift_id,
68450
68859
  action_name: "",
68860
+ action_family: cachedOverviewMetrics.action_family ?? null,
68451
68861
  action_type: cachedOverviewMetrics.action_type ?? null,
68452
68862
  monitoring_mode: cachedOverviewMetrics.monitoring_mode ?? "output",
68453
68863
  shift_start: shiftDefinition?.startTime || "",
@@ -68470,23 +68880,48 @@ var WorkspaceDetailView = ({
68470
68880
  idle_time_hourly: void 0
68471
68881
  };
68472
68882
  }, [cachedOverviewMetrics, shiftConfig?.shifts]);
68473
- const workspace = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics || overviewFallback;
68474
- const detailedWorkspaceMetrics = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics;
68883
+ const authoritativeCycleMetrics = isHistoricView ? historicMetrics : liveMetrics;
68884
+ const workspace = authoritativeCycleMetrics || cachedDetailedMetrics || overviewFallback;
68885
+ const { timezone: cycleTimeTimezone } = useTimezone({
68886
+ lineId: effectiveLineId || workspace?.line_id || void 0,
68887
+ workspaceId: workspaceId || void 0
68888
+ });
68889
+ const effectiveCycleTimeTimezone = cycleTimeTimezone || timezone;
68890
+ const detailedWorkspaceMetrics = authoritativeCycleMetrics || cachedDetailedMetrics;
68475
68891
  const cycleTimeChartData = React141.useMemo(
68476
- () => Array.isArray(workspace?.hourly_cycle_times) ? workspace.hourly_cycle_times.map((value) => {
68892
+ () => Array.isArray(authoritativeCycleMetrics?.hourly_cycle_times) ? authoritativeCycleMetrics.hourly_cycle_times.map((value) => {
68477
68893
  const numericValue = Number(value);
68478
68894
  return Number.isFinite(numericValue) ? numericValue : 0;
68479
68895
  }) : [],
68480
- [workspace?.hourly_cycle_times]
68896
+ [authoritativeCycleMetrics?.hourly_cycle_times]
68897
+ );
68898
+ const maskedCycleTimeChartData = React141.useMemo(
68899
+ () => maskFutureHourlySeries({
68900
+ data: cycleTimeChartData,
68901
+ shiftStart: authoritativeCycleMetrics?.shift_start,
68902
+ shiftEnd: authoritativeCycleMetrics?.shift_end,
68903
+ shiftDate: authoritativeCycleMetrics?.date || date || calculatedOperationalDate || null,
68904
+ timezone: effectiveCycleTimeTimezone
68905
+ }),
68906
+ [
68907
+ cycleTimeChartData,
68908
+ authoritativeCycleMetrics?.shift_start,
68909
+ authoritativeCycleMetrics?.shift_end,
68910
+ authoritativeCycleMetrics?.date,
68911
+ date,
68912
+ calculatedOperationalDate,
68913
+ effectiveCycleTimeTimezone
68914
+ ]
68481
68915
  );
68482
68916
  const cycleTimeDatasetKey = React141.useMemo(
68483
68917
  () => [
68484
- workspace?.workspace_id || workspaceId || "workspace",
68485
- date || workspace?.date || "live",
68486
- parsedShiftId ?? workspace?.shift_id ?? "current",
68487
- "hourly"
68918
+ authoritativeCycleMetrics?.workspace_id || workspaceId || "workspace",
68919
+ date || authoritativeCycleMetrics?.date || "live",
68920
+ parsedShiftId ?? authoritativeCycleMetrics?.shift_id ?? "current",
68921
+ "hourly",
68922
+ "backend"
68488
68923
  ].join(":"),
68489
- [workspace?.workspace_id, workspaceId, date, workspace?.date, parsedShiftId, workspace?.shift_id]
68924
+ [authoritativeCycleMetrics?.workspace_id, workspaceId, date, authoritativeCycleMetrics?.date, parsedShiftId, authoritativeCycleMetrics?.shift_id]
68490
68925
  );
68491
68926
  const hasWorkspaceSnapshot = Boolean(workspace);
68492
68927
  const loading = ((isHistoricView ? historicLoading : liveLoading) || isShiftConfigLoading) && !hasWorkspaceSnapshot;
@@ -68729,22 +69164,52 @@ var WorkspaceDetailView = ({
68729
69164
  return filterDataByDateKeyRange(monthlyData, range);
68730
69165
  }, [monthlyData, range]);
68731
69166
  const formattedWorkspaceName = displayName || formatWorkspaceName3(workspace?.workspace_name || "", effectiveLineId);
68732
- const workspaceActionType = workspace && "action_type" in workspace ? workspace.action_type : void 0;
68733
- const workspaceAssemblyEnabled = workspace && "line_assembly_enabled" in workspace ? workspace.line_assembly_enabled === true : false;
68734
- const isAssemblyWorkspace = workspaceActionType === "assembly" || workspaceAssemblyEnabled;
68735
- const shouldShowCycleTimeChart = !isUptimeMode && (showCycleTimeChart ?? isAssemblyWorkspace);
68736
- const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
69167
+ const workspaceCycleTimeEligibility = workspace ? {
69168
+ line_assembly_enabled: workspace.line_assembly_enabled,
69169
+ action_family: workspace.action_family,
69170
+ action_type: workspace.action_type
69171
+ } : null;
69172
+ const isAssemblyWorkspace = shouldUseAssemblyCycleTimeLayout(workspaceCycleTimeEligibility);
69173
+ const layoutMode = getWorkspaceDetailLayoutMode({
69174
+ workspace: workspace ? {
69175
+ monitoring_mode: workspace.monitoring_mode,
69176
+ line_assembly_enabled: workspace.line_assembly_enabled,
69177
+ action_family: workspace.action_family,
69178
+ action_type: workspace.action_type
69179
+ } : null,
69180
+ showCycleTimeChart
69181
+ });
69182
+ const isAssemblyCycleLayout = layoutMode === "assembly_cycle";
69183
+ const isOutputLayout = layoutMode === "output";
69184
+ const cycleTimePresentation = getCycleTimeRenderState({
69185
+ workspace: workspace ? {
69186
+ monitoring_mode: workspace.monitoring_mode,
69187
+ line_assembly_enabled: workspace.line_assembly_enabled,
69188
+ action_family: workspace.action_family,
69189
+ action_type: workspace.action_type
69190
+ } : null,
69191
+ authoritativeMetrics: authoritativeCycleMetrics ? {
69192
+ cycle_time_data_status: authoritativeCycleMetrics.cycle_time_data_status
69193
+ } : null,
69194
+ showCycleTimeChart
69195
+ });
69196
+ const shouldShowCycleTimeChart = cycleTimePresentation === "cycle_chart";
69197
+ const shouldShowCycleTimeUnavailableState = cycleTimePresentation === "cycle_unavailable";
69198
+ const shouldShowCycleTimeLoadingState = cycleTimePresentation === "chart_loading";
69199
+ const shouldShowAssemblyOverviewLoadingState = isAssemblyCycleLayout && shouldShowCycleTimeLoadingState && hasWorkspaceSnapshot;
69200
+ const showIdleBreakdownChart = !isAssemblyCycleLayout && idleTimeVlmEnabled;
69201
+ const canToggleChartIdleTime = !isUptimeMode && !shouldShowCycleTimeLoadingState && !shouldShowCycleTimeUnavailableState;
68737
69202
  const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
68738
69203
  const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
68739
- const hourlyIdleMinutes = React141.useMemo(() => {
69204
+ const rawHourlyIdleMinutes = React141.useMemo(() => {
68740
69205
  if (!shouldShowCycleTimeChart || !workspace?.idle_time_hourly || !workspace?.shift_start) return [];
68741
- const parseTimeToMinutes3 = (time2) => {
69206
+ const parseTimeToMinutes4 = (time2) => {
68742
69207
  const [h, m] = time2.split(":").map(Number);
68743
69208
  if (!Number.isFinite(h) || !Number.isFinite(m)) return 0;
68744
69209
  return h * 60 + m;
68745
69210
  };
68746
- const startTotal = parseTimeToMinutes3(workspace.shift_start);
68747
- const endTotalRaw = workspace.shift_end ? parseTimeToMinutes3(workspace.shift_end) : startTotal + 11 * 60;
69211
+ const startTotal = parseTimeToMinutes4(workspace.shift_start);
69212
+ const endTotalRaw = workspace.shift_end ? parseTimeToMinutes4(workspace.shift_end) : startTotal + 11 * 60;
68748
69213
  const endTotal = endTotalRaw <= startTotal ? endTotalRaw + 24 * 60 : endTotalRaw;
68749
69214
  const shiftDuration = Math.max(60, endTotal - startTotal);
68750
69215
  const totalHourSlots = Math.max(1, Math.ceil(shiftDuration / 60));
@@ -68770,6 +69235,42 @@ var WorkspaceDetailView = ({
68770
69235
  }
68771
69236
  return result;
68772
69237
  }, [shouldShowCycleTimeChart, workspace?.idle_time_hourly, workspace?.shift_start, workspace?.shift_end]);
69238
+ const hourlyIdleMinutes = React141.useMemo(
69239
+ () => maskFutureHourlySeries({
69240
+ data: rawHourlyIdleMinutes,
69241
+ shiftStart: workspace?.shift_start,
69242
+ shiftEnd: workspace?.shift_end,
69243
+ shiftDate: idleClipDate,
69244
+ timezone: effectiveCycleTimeTimezone
69245
+ }),
69246
+ [
69247
+ rawHourlyIdleMinutes,
69248
+ workspace?.shift_start,
69249
+ workspace?.shift_end,
69250
+ idleClipDate,
69251
+ effectiveCycleTimeTimezone
69252
+ ]
69253
+ );
69254
+ const cycleTimeUnavailableView = React141.useMemo(() => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full h-full rounded-lg border border-amber-200 bg-amber-50/70 px-6 py-5 text-center flex flex-col items-center justify-center", children: [
69255
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-base font-semibold text-amber-900", children: "Cycle data unavailable" }),
69256
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 max-w-md text-sm text-amber-800", children: "This workstation has cycle-time metrics for the selected shift, but no matching `cycle_completion` clips were found for the chart." }),
69257
+ typeof workspace?.cycle_completion_clip_count === "number" && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-3 text-xs font-medium uppercase tracking-[0.08em] text-amber-700", children: [
69258
+ "matched cycle clips: ",
69259
+ workspace.cycle_completion_clip_count
69260
+ ] })
69261
+ ] }), [workspace?.cycle_completion_clip_count]);
69262
+ const assemblyOverviewLoadingView = React141.useMemo(() => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 pb-4", "data-testid": "assembly-overview-loading-state", children: [
69263
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-sm p-6 lg:p-8", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[280px] lg:min-h-[340px] flex flex-col justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-xl mx-auto animate-pulse space-y-3", children: [
69264
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-gray-200 rounded-full w-3/4 mx-auto" }),
69265
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-gray-100 rounded-full w-full" }),
69266
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-gray-100 rounded-full w-5/6 mx-auto" })
69267
+ ] }) }) }),
69268
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4", children: [0, 1, 2].map((index) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-sm p-5", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse space-y-3", children: [
69269
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-24 bg-gray-200 rounded" }),
69270
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-10 w-20 bg-gray-100 rounded" }),
69271
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-28 bg-gray-100 rounded" })
69272
+ ] }) }, index)) })
69273
+ ] }), []);
68773
69274
  const shiftDurationMinutes = React141.useMemo(
68774
69275
  () => getShiftDurationMinutes(workspace?.shift_start, workspace?.shift_end),
68775
69276
  [workspace?.shift_start, workspace?.shift_end]
@@ -68824,7 +69325,7 @@ var WorkspaceDetailView = ({
68824
69325
  }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time]);
68825
69326
  const overviewTabLabel = isUptimeMode ? "Utilization" : "Efficiency";
68826
69327
  const idleClipFetchEnabled = Boolean(
68827
- workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && !shouldShowCycleTimeChart && !isUptimeMode
69328
+ workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && isOutputLayout
68828
69329
  );
68829
69330
  const {
68830
69331
  idleClips: idleTimeClips,
@@ -68895,7 +69396,7 @@ var WorkspaceDetailView = ({
68895
69396
  }
68896
69397
  }
68897
69398
  };
68898
- const getShiftIcon = (shiftType) => {
69399
+ const getShiftIcon2 = (shiftType) => {
68899
69400
  const shiftTypeLower = shiftType?.toLowerCase() || "";
68900
69401
  if (shiftTypeLower.includes("day") || shiftTypeLower.includes("morning")) {
68901
69402
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
@@ -69044,7 +69545,7 @@ var WorkspaceDetailView = ({
69044
69545
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: (() => {
69045
69546
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
69046
69547
  const shiftName = shift2?.shiftName || (selectedShift === 0 ? "Day Shift" : "Night Shift");
69047
- return getShiftIcon(shiftName);
69548
+ return getShiftIcon2(shiftName);
69048
69549
  })() }),
69049
69550
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: (() => {
69050
69551
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
@@ -69072,7 +69573,7 @@ var WorkspaceDetailView = ({
69072
69573
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: (() => {
69073
69574
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
69074
69575
  const shiftName = shift2?.shiftName || (selectedShift === 0 ? "Day Shift" : "Night Shift");
69075
- return getShiftIcon(shiftName);
69576
+ return getShiftIcon2(shiftName);
69076
69577
  })() }),
69077
69578
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: (() => {
69078
69579
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
@@ -69085,7 +69586,7 @@ var WorkspaceDetailView = ({
69085
69586
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
69086
69587
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-gray-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: formatISTDate2(new Date(workspace.date)) }) }),
69087
69588
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
69088
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(workspace.shift_type) }),
69589
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2(workspace.shift_type) }),
69089
69590
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: workspace.shift_type })
69090
69591
  ] }),
69091
69592
  !date && !shift && !usingFallbackData ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(LiveTimer, {}) }) }) : date ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-blue-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-blue-700", children: getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00") }) }) : usingFallbackData ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-amber-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-amber-700", children: getDaysDifference(workspace.date, timezone, dashboardConfig?.shiftConfig?.dayShift?.startTime || "06:00") }) }) : null
@@ -69116,7 +69617,7 @@ var WorkspaceDetailView = ({
69116
69617
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
69117
69618
  ] }),
69118
69619
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
69119
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(workspace.shift_type) }),
69620
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(workspace.shift_type) }),
69120
69621
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
69121
69622
  workspace.shift_type.replace(/ Shift$/i, ""),
69122
69623
  " Shift"
@@ -69235,9 +69736,9 @@ var WorkspaceDetailView = ({
69235
69736
  ] })
69236
69737
  ] }),
69237
69738
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4", children: [
69238
- activeTab === "overview" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full lg:h-[calc(100vh-12rem)] overflow-y-auto lg:min-h-0 pb-4", children: [
69739
+ activeTab === "overview" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col h-full lg:h-[calc(100vh-12rem)] overflow-y-auto lg:min-h-0 pb-4", children: shouldShowAssemblyOverviewLoadingState ? assemblyOverviewLoadingView : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
69239
69740
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "block lg:hidden space-y-6 pb-6", children: [
69240
- !shouldShowCycleTimeChart && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69741
+ isOutputLayout && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69241
69742
  motion.div,
69242
69743
  {
69243
69744
  className: "bg-white rounded-lg shadow-sm p-6 h-[300px]",
@@ -69265,8 +69766,8 @@ var WorkspaceDetailView = ({
69265
69766
  animate: "animate",
69266
69767
  children: [
69267
69768
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
69268
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
69269
- !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
69769
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : isAssemblyCycleLayout ? "Cycle time trend" : "Hourly Output" }),
69770
+ canToggleChartIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
69270
69771
  "button",
69271
69772
  {
69272
69773
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69295,19 +69796,19 @@ var WorkspaceDetailView = ({
69295
69796
  timezone,
69296
69797
  elapsedMinutes: elapsedShiftMinutes
69297
69798
  }
69298
- ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69799
+ ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69299
69800
  CycleTimeOverTimeChart,
69300
69801
  {
69301
- data: cycleTimeChartData,
69302
- idealCycleTime: workspace.ideal_cycle_time || 0,
69303
- shiftStart: workspace.shift_start || "",
69304
- shiftEnd: workspace.shift_end || "",
69802
+ data: maskedCycleTimeChartData,
69803
+ idealCycleTime: authoritativeCycleMetrics?.ideal_cycle_time || 0,
69804
+ shiftStart: authoritativeCycleMetrics?.shift_start || "",
69805
+ shiftEnd: authoritativeCycleMetrics?.shift_end || "",
69305
69806
  xAxisMode: "hourly",
69306
69807
  datasetKey: cycleTimeDatasetKey,
69307
69808
  showIdleTime: showChartIdleTime,
69308
69809
  idleTimeData: hourlyIdleMinutes
69309
69810
  }
69310
- ) : /* @__PURE__ */ jsxRuntime.jsx(
69811
+ ) : null : /* @__PURE__ */ jsxRuntime.jsx(
69311
69812
  HourlyOutputChart2,
69312
69813
  {
69313
69814
  data: workspace.hourly_action_counts || [],
@@ -69327,7 +69828,7 @@ var WorkspaceDetailView = ({
69327
69828
  ]
69328
69829
  }
69329
69830
  ),
69330
- !shouldShowCycleTimeChart && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
69831
+ showIdleBreakdownChart && /* @__PURE__ */ jsxRuntime.jsxs(
69331
69832
  motion.div,
69332
69833
  {
69333
69834
  className: "bg-white rounded-lg shadow-sm p-4 h-[300px]",
@@ -69347,7 +69848,7 @@ var WorkspaceDetailView = ({
69347
69848
  ]
69348
69849
  }
69349
69850
  ),
69350
- isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData }) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69851
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData }) : isAssemblyCycleLayout ? /* @__PURE__ */ jsxRuntime.jsx(
69351
69852
  WorkspaceCycleTimeMetricCards,
69352
69853
  {
69353
69854
  workspace,
@@ -69367,7 +69868,7 @@ var WorkspaceDetailView = ({
69367
69868
  desktopTopSectionClass
69368
69869
  ),
69369
69870
  children: [
69370
- !shouldShowCycleTimeChart && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69871
+ isOutputLayout && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69371
69872
  motion.div,
69372
69873
  {
69373
69874
  className: "bg-white rounded-lg shadow-sm p-4 lg:col-span-2 flex flex-col min-h-0",
@@ -69391,15 +69892,15 @@ var WorkspaceDetailView = ({
69391
69892
  {
69392
69893
  className: clsx(
69393
69894
  "bg-white rounded-lg shadow-sm p-4 flex flex-col min-h-0",
69394
- isUptimeMode && showIdleBreakdownChart ? "lg:col-span-2" : shouldShowCycleTimeChart || isUptimeMode ? "lg:col-span-10" : idleTimeVlmEnabled ? "lg:col-span-6" : "lg:col-span-8"
69895
+ isUptimeMode && showIdleBreakdownChart ? "lg:col-span-2" : isAssemblyCycleLayout || isUptimeMode ? "lg:col-span-10" : idleTimeVlmEnabled ? "lg:col-span-6" : "lg:col-span-8"
69395
69896
  ),
69396
69897
  variants: chartCardVariants,
69397
69898
  initial: "initial",
69398
69899
  animate: "animate",
69399
69900
  children: [
69400
69901
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mb-4 flex-none", children: [
69401
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
69402
- !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsx(
69902
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : isAssemblyCycleLayout ? "Cycle time trend" : "Hourly Output" }),
69903
+ canToggleChartIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
69403
69904
  "button",
69404
69905
  {
69405
69906
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69424,19 +69925,19 @@ var WorkspaceDetailView = ({
69424
69925
  timezone,
69425
69926
  elapsedMinutes: elapsedShiftMinutes
69426
69927
  }
69427
- ) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69928
+ ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69428
69929
  CycleTimeOverTimeChart,
69429
69930
  {
69430
- data: cycleTimeChartData,
69431
- idealCycleTime: workspace.ideal_cycle_time || 0,
69432
- shiftStart: workspace.shift_start || "",
69433
- shiftEnd: workspace.shift_end || "",
69931
+ data: maskedCycleTimeChartData,
69932
+ idealCycleTime: authoritativeCycleMetrics?.ideal_cycle_time || 0,
69933
+ shiftStart: authoritativeCycleMetrics?.shift_start || "",
69934
+ shiftEnd: authoritativeCycleMetrics?.shift_end || "",
69434
69935
  xAxisMode: "hourly",
69435
69936
  datasetKey: cycleTimeDatasetKey,
69436
69937
  showIdleTime: showChartIdleTime,
69437
69938
  idleTimeData: hourlyIdleMinutes
69438
69939
  }
69439
- ) : /* @__PURE__ */ jsxRuntime.jsx(
69940
+ ) : null : /* @__PURE__ */ jsxRuntime.jsx(
69440
69941
  HourlyOutputChart2,
69441
69942
  {
69442
69943
  data: workspace.hourly_action_counts || [],
@@ -69454,7 +69955,7 @@ var WorkspaceDetailView = ({
69454
69955
  ]
69455
69956
  }
69456
69957
  ),
69457
- !shouldShowCycleTimeChart && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
69958
+ showIdleBreakdownChart && /* @__PURE__ */ jsxRuntime.jsxs(
69458
69959
  motion.div,
69459
69960
  {
69460
69961
  className: clsx(
@@ -69480,7 +69981,7 @@ var WorkspaceDetailView = ({
69480
69981
  ]
69481
69982
  }
69482
69983
  ),
69483
- isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData, className: "flex-1" }) }) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69984
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData, className: "flex-1" }) }) : isAssemblyCycleLayout ? /* @__PURE__ */ jsxRuntime.jsx(
69484
69985
  WorkspaceCycleTimeMetricCards,
69485
69986
  {
69486
69987
  workspace,
@@ -69491,7 +69992,7 @@ var WorkspaceDetailView = ({
69491
69992
  }
69492
69993
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
69493
69994
  ] })
69494
- ] }),
69995
+ ] }) }),
69495
69996
  activeTab === "monthly_history" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-[calc(100vh-10rem)] overflow-y-auto px-2 sm:px-4 lg:px-0", children: [
69496
69997
  usingFallbackData && !date && !shift && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-3 sm:mb-4 bg-amber-50 border border-amber-200 rounded-lg px-3 sm:px-4 py-2 sm:py-3 text-amber-800", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs sm:text-sm font-medium flex items-center", children: [
69497
69998
  /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-4 w-4 sm:h-5 sm:w-5 mr-2", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", clipRule: "evenodd" }) }),
@@ -72123,7 +72624,7 @@ var normalizeLabel = (value) => {
72123
72624
  const trimmed = value.trim();
72124
72625
  return trimmed.length > 0 ? trimmed : void 0;
72125
72626
  };
72126
- var toFiniteNumber = (value) => {
72627
+ var toFiniteNumber2 = (value) => {
72127
72628
  if (typeof value === "number" && Number.isFinite(value)) return value;
72128
72629
  if (typeof value === "string" && value.trim().length > 0) {
72129
72630
  const parsed = Number(value);
@@ -72157,7 +72658,7 @@ var formatImprovementPieceGain = (pcsGain) => {
72157
72658
  var getPositiveImprovementGain = ({
72158
72659
  estimated_gain_pieces
72159
72660
  }) => {
72160
- const issueGain = toFiniteNumber(estimated_gain_pieces);
72661
+ const issueGain = toFiniteNumber2(estimated_gain_pieces);
72161
72662
  return {
72162
72663
  pcsGain: issueGain !== null && issueGain > 0 ? issueGain : null
72163
72664
  };
@@ -72165,7 +72666,7 @@ var getPositiveImprovementGain = ({
72165
72666
  var getImprovementPcsGainSortValue = (input) => {
72166
72667
  const { pcsGain } = getPositiveImprovementGain(input);
72167
72668
  if (pcsGain !== null) return pcsGain;
72168
- const raw = toFiniteNumber(input.estimated_gain_pieces);
72669
+ const raw = toFiniteNumber2(input.estimated_gain_pieces);
72169
72670
  return raw ?? null;
72170
72671
  };
72171
72672
  var getImprovementDisplayMetadata = ({
@@ -72185,7 +72686,7 @@ var getImprovementDisplayMetadata = ({
72185
72686
  metadataLabel: [workstationLabel, lineLabel, supervisorLabel].join(" \xB7 ")
72186
72687
  };
72187
72688
  };
72188
- var toFiniteNumber2 = (value) => {
72689
+ var toFiniteNumber3 = (value) => {
72189
72690
  if (typeof value === "number" && Number.isFinite(value)) return value;
72190
72691
  if (typeof value === "string" && value.trim().length > 0) {
72191
72692
  const parsed = Number(value);
@@ -72213,15 +72714,15 @@ var compareImprovementRecommendationPriority = (left, right) => {
72213
72714
  if (leftIndustrial !== rightIndustrial) {
72214
72715
  return rightIndustrial - leftIndustrial;
72215
72716
  }
72216
- const leftGain = toFiniteNumber2(left.estimated_gain_pieces);
72217
- const rightGain = toFiniteNumber2(right.estimated_gain_pieces);
72717
+ const leftGain = toFiniteNumber3(left.estimated_gain_pieces);
72718
+ const rightGain = toFiniteNumber3(right.estimated_gain_pieces);
72218
72719
  if (leftGain !== rightGain) {
72219
72720
  if (leftGain === null) return 1;
72220
72721
  if (rightGain === null) return -1;
72221
72722
  return rightGain - leftGain;
72222
72723
  }
72223
- const leftRatio = toFiniteNumber2(left.gain_to_target_ratio);
72224
- const rightRatio = toFiniteNumber2(right.gain_to_target_ratio);
72724
+ const leftRatio = toFiniteNumber3(left.gain_to_target_ratio);
72725
+ const rightRatio = toFiniteNumber3(right.gain_to_target_ratio);
72225
72726
  if (leftRatio !== rightRatio) {
72226
72727
  if (leftRatio === null) return 1;
72227
72728
  if (rightRatio === null) return -1;
@@ -75526,6 +76027,7 @@ var EMPTY_OVERVIEW_POOREST_LINES = {
75526
76027
  };
75527
76028
  var EMPTY_OVERVIEW_TREND = {
75528
76029
  shift_mode: "all",
76030
+ granularity: "day",
75529
76031
  points: []
75530
76032
  };
75531
76033
  var EMPTY_IDLE_BREAKDOWN = [];
@@ -75598,8 +76100,11 @@ var normalizePoorestLines = (value) => ({
75598
76100
  });
75599
76101
  var normalizeTrend = (value) => ({
75600
76102
  shift_mode: value?.shift_mode || "all",
76103
+ granularity: value?.granularity === "hour" ? "hour" : "day",
75601
76104
  points: (value?.points || []).map((point) => ({
75602
76105
  date: point?.date,
76106
+ label: point?.label,
76107
+ hour_index: normalizeNumber(point?.hour_index),
75603
76108
  avg_efficiency: normalizeNumber(point?.avg_efficiency)
75604
76109
  }))
75605
76110
  });
@@ -75941,17 +76446,25 @@ var formatSignedIdleDuration = (seconds) => {
75941
76446
  const sign = seconds > 0 ? "+" : "-";
75942
76447
  return `${sign}${formatIdleDuration(Math.abs(seconds))}`;
75943
76448
  };
75944
- var formatComparisonWindow = (dayCount, comparisonStrategy) => {
76449
+ var formatComparisonWindow = ({
76450
+ currentDayCount,
76451
+ previousDayCount,
76452
+ comparisonStrategy,
76453
+ shiftMode
76454
+ }) => {
75945
76455
  if (comparisonStrategy === "previous_full_week") return "last week";
75946
- if (!dayCount || !Number.isFinite(dayCount)) return "previous range";
75947
- return `previous ${dayCount} ${dayCount === 1 ? "day" : "days"}`;
76456
+ if (comparisonStrategy === "matched_range" && shiftMode !== "all" && currentDayCount === 1 && previousDayCount === 1) {
76457
+ return "previous day";
76458
+ }
76459
+ if (!previousDayCount || !Number.isFinite(previousDayCount)) return "previous range";
76460
+ return `previous ${previousDayCount} ${previousDayCount === 1 ? "day" : "days"}`;
75948
76461
  };
75949
76462
  var buildDeltaBadge = (delta, options) => {
75950
76463
  if (delta === null || delta === void 0 || !Number.isFinite(delta)) {
75951
76464
  return {
75952
76465
  icon: null,
75953
- className: "bg-slate-100 text-slate-500",
75954
- text: `No ${options.comparisonLabel} baseline`
76466
+ className: "bg-slate-100 text-slate-400",
76467
+ text: "\u2014"
75955
76468
  };
75956
76469
  }
75957
76470
  const direction = delta >= 0 ? "up" : "down";
@@ -75962,6 +76475,25 @@ var buildDeltaBadge = (delta, options) => {
75962
76475
  text: `${options.formatter(delta)} vs ${options.comparisonLabel}`
75963
76476
  };
75964
76477
  };
76478
+ var normalizeShiftLabel = (shiftName, shiftMode) => {
76479
+ const trimmedName = shiftName?.trim();
76480
+ if (trimmedName) {
76481
+ return /shift/i.test(trimmedName) ? trimmedName : `${trimmedName} Shift`;
76482
+ }
76483
+ if (shiftMode === "night") return "Night Shift";
76484
+ return "Day Shift";
76485
+ };
76486
+ var getShiftIcon = (shiftName, shiftMode) => {
76487
+ const normalizedName = (shiftName || "").toLowerCase();
76488
+ const normalizedMode = shiftMode || "day";
76489
+ if (normalizedName.includes("day") || normalizedName.includes("morning") || normalizedMode === "day") {
76490
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
76491
+ }
76492
+ if (normalizedName.includes("night") || normalizedName.includes("evening") || normalizedMode === "night") {
76493
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" }) });
76494
+ }
76495
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" }) });
76496
+ };
75965
76497
  var buildLineDeltaTone = (delta, comparisonLabel) => {
75966
76498
  if (delta === null || delta === void 0 || !Number.isFinite(delta)) {
75967
76499
  return {
@@ -76017,6 +76549,8 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76017
76549
  dateRange,
76018
76550
  displayDateRange,
76019
76551
  trendMode,
76552
+ isLiveScope,
76553
+ liveShiftName,
76020
76554
  lineOptions,
76021
76555
  supervisorOptions,
76022
76556
  selectedSupervisorId,
@@ -76030,6 +76564,14 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76030
76564
  }) => {
76031
76565
  bumpRenderCounter();
76032
76566
  const subtitleRange = displayDateRange || dateRange;
76567
+ const liveShiftLabel = React141__namespace.default.useMemo(
76568
+ () => normalizeShiftLabel(liveShiftName, trendMode),
76569
+ [liveShiftName, trendMode]
76570
+ );
76571
+ const liveShiftIcon = React141__namespace.default.useMemo(
76572
+ () => getShiftIcon(liveShiftName, trendMode),
76573
+ [liveShiftName, trendMode]
76574
+ );
76033
76575
  const [isFilterOpen, setIsFilterOpen] = React141__namespace.default.useState(false);
76034
76576
  const [isLinesDropdownOpen, setIsLinesDropdownOpen] = React141__namespace.default.useState(false);
76035
76577
  const filterRef = React141__namespace.default.useRef(null);
@@ -76146,9 +76688,25 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76146
76688
  className: "flex-shrink-0 -ml-1"
76147
76689
  }
76148
76690
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex-shrink-0" }),
76149
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center", children: [
76150
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900 text-center px-1 truncate max-w-[200px]", children: "Operations Overview" }),
76151
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium text-slate-500 text-center mt-0.5", children: mobileSubtitle })
76691
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center min-w-0", children: [
76692
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-1.5 min-w-0 max-w-[240px]", children: [
76693
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900 leading-tight text-center truncate", children: "Operations Overview" }),
76694
+ isLiveScope ? /* @__PURE__ */ jsxRuntime.jsx(
76695
+ "div",
76696
+ {
76697
+ "data-testid": "operations-overview-live-indicator",
76698
+ className: "h-2 w-2 rounded-full bg-emerald-500 animate-pulse ring-2 ring-emerald-500/20 flex-shrink-0"
76699
+ }
76700
+ ) : null
76701
+ ] }),
76702
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex flex-wrap items-center justify-center gap-x-2 gap-y-1 text-[11px] font-medium text-slate-500 min-w-0", children: [
76703
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-center", children: mobileSubtitle }),
76704
+ isLiveScope ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-300", children: "|" }) : null,
76705
+ isLiveScope ? /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "operations-overview-live-meta", className: "inline-flex items-center gap-1.5 text-gray-600 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 justify-center truncate", children: [
76706
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75 shrink-0", children: liveShiftIcon }),
76707
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: liveShiftLabel })
76708
+ ] }) }) : null
76709
+ ] })
76152
76710
  ] }),
76153
76711
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 flex items-center gap-1.5", children: [
76154
76712
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -76167,22 +76725,40 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76167
76725
  {
76168
76726
  ref: mobileFilterButtonRef,
76169
76727
  onClick: handleFilterToggle,
76170
- className: `p-2 rounded-full transition-colors relative ${isFilterOpen || activeFilterCount > 0 ? "bg-blue-50" : "active:bg-gray-100"}`,
76728
+ className: `p-2 rounded-full transition-colors relative ${isFilterOpen || activeFilterCount > 0 ? "bg-slate-100 text-slate-700" : "active:bg-gray-100 text-slate-600"}`,
76171
76729
  "aria-label": "Open filters",
76172
76730
  children: [
76173
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-5 h-5 ${activeFilterCount > 0 ? "text-blue-600" : "text-gray-700"}` }),
76174
- activeFilterCount > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex items-center justify-center w-4 h-4 bg-blue-600 text-white text-[10px] rounded-full font-bold", children: activeFilterCount }) : null
76731
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: "w-5 h-5" }),
76732
+ activeFilterCount > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex items-center justify-center w-4 h-4 bg-slate-700 text-white text-[10px] rounded-full font-bold", children: activeFilterCount }) : null
76175
76733
  ]
76176
76734
  }
76177
76735
  )
76178
76736
  ] })
76179
76737
  ] }) }),
76180
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center relative min-h-[56px]", children: [
76181
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-1/2 -translate-x-1/2 flex flex-col items-center pointer-events-none", children: [
76182
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl md:text-3xl lg:text-4xl font-semibold text-gray-900 tracking-tight text-center pointer-events-auto leading-tight mb-0.5", children: "Operations Overview" }),
76183
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium text-slate-500 text-center pointer-events-auto", children: desktopSubtitle })
76738
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center relative min-h-[64px]", children: [
76739
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-1/2 -translate-x-1/2 flex flex-col items-center min-w-0 max-w-[min(70vw,720px)]", children: [
76740
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 min-w-0 max-w-full", children: [
76741
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl md:text-3xl lg:text-4xl font-semibold text-gray-900 tracking-tight leading-tight text-center truncate max-w-full", children: "Operations Overview" }),
76742
+ isLiveScope ? /* @__PURE__ */ jsxRuntime.jsx(
76743
+ "div",
76744
+ {
76745
+ "data-testid": "operations-overview-live-indicator",
76746
+ className: "h-1.5 w-1.5 md:h-2 md:w-2 rounded-full bg-green-500 animate-pulse ring-2 ring-green-500/30 ring-offset-1 flex-shrink-0"
76747
+ }
76748
+ ) : null
76749
+ ] }),
76750
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center justify-center gap-x-3 gap-y-1 text-xs sm:text-sm font-medium text-slate-500 text-center", children: [
76751
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: desktopSubtitle }),
76752
+ isLiveScope ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
76753
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-300", children: "|" }),
76754
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "data-testid": "operations-overview-live-meta", className: "inline-flex items-center gap-1.5 text-gray-600", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5 justify-center", children: [
76755
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75", children: liveShiftIcon }),
76756
+ liveShiftLabel
76757
+ ] }) })
76758
+ ] }) : null
76759
+ ] })
76184
76760
  ] }),
76185
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 flex items-center gap-3", children: [
76761
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 flex items-center gap-3 shrink-0", children: [
76186
76762
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(
76187
76763
  MonthlyRangeFilter_default,
76188
76764
  {
@@ -76199,12 +76775,12 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76199
76775
  {
76200
76776
  ref: filterButtonRef,
76201
76777
  onClick: handleFilterToggle,
76202
- className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || activeFilterCount > 0 ? "border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-500" : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50"}`,
76778
+ className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || activeFilterCount > 0 ? "border-slate-300 bg-slate-50 text-slate-700" : "border-slate-200 bg-white text-slate-600 hover:bg-slate-50 hover:text-slate-700"}`,
76203
76779
  "aria-label": "Open filters",
76204
76780
  children: [
76205
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-[18px] h-[18px] ${activeFilterCount > 0 ? "text-blue-600" : "text-slate-500"}` }),
76781
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-[18px] h-[18px] ${isFilterOpen || activeFilterCount > 0 ? "text-slate-500" : "text-slate-400"}` }),
76206
76782
  "Filters",
76207
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-4 h-4 ml-0.5 transition-transform duration-200 ${isFilterOpen ? "rotate-180" : ""}` })
76783
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-4 h-4 ml-0.5 text-slate-400 transition-transform duration-200 ${isFilterOpen ? "rotate-180" : ""}` })
76208
76784
  ]
76209
76785
  }
76210
76786
  )
@@ -76332,11 +76908,18 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76332
76908
  const snapshot = useOperationsOverviewSnapshot(store);
76333
76909
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
76334
76910
  const comparisonLabel = React141__namespace.default.useMemo(() => {
76335
- return formatComparisonWindow(
76336
- scope.previous_range?.day_count ?? null,
76337
- scope.comparison_strategy
76338
- );
76339
- }, [scope.comparison_strategy, scope.previous_range?.day_count]);
76911
+ return formatComparisonWindow({
76912
+ currentDayCount: scope.current_range?.day_count ?? null,
76913
+ previousDayCount: scope.previous_range?.day_count ?? null,
76914
+ comparisonStrategy: scope.comparison_strategy,
76915
+ shiftMode: scope.shift_mode
76916
+ });
76917
+ }, [
76918
+ scope.comparison_strategy,
76919
+ scope.current_range?.day_count,
76920
+ scope.previous_range?.day_count,
76921
+ scope.shift_mode
76922
+ ]);
76340
76923
  const [isIdleContributorsOpen, setIsIdleContributorsOpen] = React141__namespace.default.useState(false);
76341
76924
  const [isIdleContributorsPinned, setIsIdleContributorsPinned] = React141__namespace.default.useState(false);
76342
76925
  const idleContributorsRef = React141__namespace.default.useRef(null);
@@ -76426,10 +77009,17 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76426
77009
  roundOne(snapshot.data.summary.plant_efficiency.current),
76427
77010
  "%"
76428
77011
  ] }),
76429
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 px-2.5 py-1 rounded-full ${plantEfficiencyBadge.className}`, children: [
76430
- plantEfficiencyBadge.icon === "up" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3.5 h-3.5", strokeWidth: 2.5 }) : plantEfficiencyBadge.icon === "down" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3.5 h-3.5", strokeWidth: 2.5 }) : null,
76431
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium", children: plantEfficiencyBadge.text })
76432
- ] })
77012
+ /* @__PURE__ */ jsxRuntime.jsxs(
77013
+ "div",
77014
+ {
77015
+ "data-testid": "operations-overview-efficiency-delta",
77016
+ className: `flex items-center gap-1 px-2.5 py-1 rounded-full ${plantEfficiencyBadge.className}`,
77017
+ children: [
77018
+ plantEfficiencyBadge.icon === "up" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3.5 h-3.5", strokeWidth: 2.5 }) : plantEfficiencyBadge.icon === "down" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3.5 h-3.5", strokeWidth: 2.5 }) : null,
77019
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium", children: plantEfficiencyBadge.text })
77020
+ ]
77021
+ }
77022
+ )
76433
77023
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-sm text-slate-400", children: "No efficiency data available" })
76434
77024
  ] }),
76435
77025
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -76475,10 +77065,17 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76475
77065
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Idle Time per Workstation" }) }),
76476
77066
  showSnapshotSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewMetricCardSkeleton, {}) : snapshot.data.summary.avg_idle_per_workstation?.current_seconds !== null && snapshot.data.summary.avg_idle_per_workstation?.current_seconds !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 sm:gap-3 mt-1", children: [
76477
77067
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl sm:text-3xl font-bold text-slate-800 tracking-tight", children: formatIdleDuration(snapshot.data.summary.avg_idle_per_workstation.current_seconds) }),
76478
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 px-2.5 py-1 rounded-full ${idleBadge.className}`, children: [
76479
- idleBadge.icon === "up" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3.5 h-3.5", strokeWidth: 2.5 }) : idleBadge.icon === "down" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3.5 h-3.5", strokeWidth: 2.5 }) : null,
76480
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium", children: idleBadge.text })
76481
- ] })
77068
+ /* @__PURE__ */ jsxRuntime.jsxs(
77069
+ "div",
77070
+ {
77071
+ "data-testid": "operations-overview-idle-delta",
77072
+ className: `flex items-center gap-1 px-2.5 py-1 rounded-full ${idleBadge.className}`,
77073
+ children: [
77074
+ idleBadge.icon === "up" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "w-3.5 h-3.5", strokeWidth: 2.5 }) : idleBadge.icon === "down" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDown, { className: "w-3.5 h-3.5", strokeWidth: 2.5 }) : null,
77075
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium", children: idleBadge.text })
77076
+ ]
77077
+ }
77078
+ )
76482
77079
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-sm text-slate-400", children: "No idle time data available" })
76483
77080
  ]
76484
77081
  }
@@ -76541,11 +77138,18 @@ var PoorestPerformersCard = React141__namespace.default.memo(({
76541
77138
  }
76542
77139
  }, [availableLineModes?.has_output, availableLineModes?.has_uptime, poorestLineMode]);
76543
77140
  const comparisonLabel = React141__namespace.default.useMemo(() => {
76544
- return formatComparisonWindow(
76545
- scope.previous_range?.day_count ?? null,
76546
- scope.comparison_strategy
76547
- );
76548
- }, [scope.comparison_strategy, scope.previous_range?.day_count]);
77141
+ return formatComparisonWindow({
77142
+ currentDayCount: scope.current_range?.day_count ?? null,
77143
+ previousDayCount: scope.previous_range?.day_count ?? null,
77144
+ comparisonStrategy: scope.comparison_strategy,
77145
+ shiftMode: scope.shift_mode
77146
+ });
77147
+ }, [
77148
+ scope.comparison_strategy,
77149
+ scope.current_range?.day_count,
77150
+ scope.previous_range?.day_count,
77151
+ scope.shift_mode
77152
+ ]);
76549
77153
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
76550
77154
  const mergedPoorestLines = React141__namespace.default.useMemo(() => {
76551
77155
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
@@ -76702,7 +77306,8 @@ IdleBreakdownCard.displayName = "IdleBreakdownCard";
76702
77306
  var EfficiencyTrendCard = React141__namespace.default.memo(({
76703
77307
  store,
76704
77308
  dateRange,
76705
- appTimezone
77309
+ appTimezone,
77310
+ hourlyLabelStartTime
76706
77311
  }) => {
76707
77312
  bumpRenderCounter();
76708
77313
  const trend = useOperationsOverviewTrend(store);
@@ -76712,7 +77317,41 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
76712
77317
  );
76713
77318
  const isCurrentWeekToDateRange = dateRange.startKey === currentWeekRange.startKey && dateRange.endKey === currentWeekRange.endKey;
76714
77319
  const showInitialSkeleton = trend.loading && trend.lastUpdated === null;
77320
+ const isHourlyTrend = trend.data.granularity === "hour";
76715
77321
  const trendData = React141__namespace.default.useMemo(() => {
77322
+ if (isHourlyTrend) {
77323
+ return (trend.data.points || []).map((point, index) => ({
77324
+ name: (() => {
77325
+ const rawLabel = point.label?.trim() || "";
77326
+ if (!hourlyLabelStartTime) {
77327
+ return rawLabel || `Hour ${index + 1}`;
77328
+ }
77329
+ if (rawLabel && !/^Hour\s+\d+$/i.test(rawLabel)) {
77330
+ return rawLabel;
77331
+ }
77332
+ const hourIndex = typeof point.hour_index === "number" ? point.hour_index : index;
77333
+ const [hoursPart, minutesPart] = hourlyLabelStartTime.split(":");
77334
+ const startHours = Number(hoursPart);
77335
+ const startMinutes = Number(minutesPart);
77336
+ if (!Number.isFinite(startHours) || !Number.isFinite(startMinutes)) {
77337
+ return rawLabel || `Hour ${index + 1}`;
77338
+ }
77339
+ const totalMinutes = startHours * 60 + startMinutes + hourIndex * 60;
77340
+ const hour24 = Math.floor(totalMinutes / 60) % 24;
77341
+ const minutes = totalMinutes % 60;
77342
+ const suffix = hour24 < 12 ? "AM" : "PM";
77343
+ const hour12 = hour24 % 12 || 12;
77344
+ if (minutes === 0) {
77345
+ return `${hour12} ${suffix}`;
77346
+ }
77347
+ return `${hour12}:${minutes.toString().padStart(2, "0")} ${suffix}`;
77348
+ })(),
77349
+ efficiency: (() => {
77350
+ const value = toNumber3(point.avg_efficiency);
77351
+ return value === null ? void 0 : value;
77352
+ })()
77353
+ }));
77354
+ }
76716
77355
  const pointsByDate = new Map(
76717
77356
  (trend.data.points || []).flatMap((point) => {
76718
77357
  if (!point.date) return [];
@@ -76750,12 +77389,13 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
76750
77389
  })()
76751
77390
  };
76752
77391
  });
76753
- }, [currentWeekRange.startKey, isCurrentWeekToDateRange, trend.data.points]);
77392
+ }, [currentWeekRange.startKey, hourlyLabelStartTime, isCurrentWeekToDateRange, isHourlyTrend, trend.data.points]);
76754
77393
  const trendTooltipLabelFormatter = React141__namespace.default.useCallback((label, payload) => {
77394
+ if (isHourlyTrend) return label;
76755
77395
  const dayOfWeek = payload?.[0]?.payload?.dayOfWeek;
76756
77396
  if (!dayOfWeek || typeof label !== "string") return label;
76757
77397
  return `${label} (${dayOfWeek})`;
76758
- }, []);
77398
+ }, [isHourlyTrend]);
76759
77399
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-xl shadow-[0_2px_10px_-3px_rgba(6,81,237,0.1)] border border-slate-100 flex flex-col overflow-hidden text-left", children: [
76760
77400
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-5 flex-none flex justify-between items-center border-b border-slate-50/50", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Efficiency Trend" }) }),
76761
77401
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-[250px] w-full p-4 pt-4 relative", children: showInitialSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewChartSkeleton, {}) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 pb-2 pr-4 pl-1", children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -76909,7 +77549,8 @@ var useOperationsOverviewRefresh = ({
76909
77549
  endKey,
76910
77550
  trendMode,
76911
77551
  comparisonStrategy,
76912
- isLiveScope
77552
+ isLiveScope,
77553
+ enabled = true
76913
77554
  }) => {
76914
77555
  const lineIdsKey = React141__namespace.default.useMemo(() => lineIds.join(","), [lineIds]);
76915
77556
  const scopeSignature = React141__namespace.default.useMemo(
@@ -76955,7 +77596,7 @@ var useOperationsOverviewRefresh = ({
76955
77596
  }, []);
76956
77597
  const runRefresh = React141__namespace.default.useCallback(
76957
77598
  async (section, begin, onSuccess, onError, request, reason) => {
76958
- if (!supabase || !companyId || lineIds.length === 0) return;
77599
+ if (!enabled || !supabase || !companyId || lineIds.length === 0) return;
76959
77600
  const requestId = requestIdsRef.current[section] + 1;
76960
77601
  requestIdsRef.current[section] = requestId;
76961
77602
  controllersRef.current[section]?.abort();
@@ -76975,7 +77616,7 @@ var useOperationsOverviewRefresh = ({
76975
77616
  onError(error instanceof Error ? error.message : `Failed to refresh ${section}`);
76976
77617
  }
76977
77618
  },
76978
- [companyId, lineIds.length, supabase]
77619
+ [companyId, enabled, lineIds.length, supabase]
76979
77620
  );
76980
77621
  const refreshSnapshot = React141__namespace.default.useCallback(
76981
77622
  async (reason) => {
@@ -77145,6 +77786,12 @@ var useOperationsOverviewRefresh = ({
77145
77786
  });
77146
77787
  }, [refreshAll, startPolling, stopPolling]);
77147
77788
  React141__namespace.default.useEffect(() => {
77789
+ if (!enabled) {
77790
+ stopPolling("disabled");
77791
+ abortAll();
77792
+ store.reset();
77793
+ return;
77794
+ }
77148
77795
  if (!supabase || !companyId || lineIds.length === 0) {
77149
77796
  stopPolling("scope_invalid");
77150
77797
  abortAll();
@@ -77152,9 +77799,9 @@ var useOperationsOverviewRefresh = ({
77152
77799
  return;
77153
77800
  }
77154
77801
  void refreshAll("scope_change");
77155
- }, [abortAll, companyId, lineIds.length, refreshAll, scopeSignature, stopPolling, store, supabase]);
77802
+ }, [abortAll, companyId, enabled, lineIds.length, refreshAll, scopeSignature, stopPolling, store, supabase]);
77156
77803
  React141__namespace.default.useEffect(() => {
77157
- if (!isLiveScope || !supabase || !companyId || lineIds.length === 0) {
77804
+ if (!enabled || !isLiveScope || !supabase || !companyId || lineIds.length === 0) {
77158
77805
  isPageActiveRef.current = false;
77159
77806
  stopPolling("live_scope_disabled");
77160
77807
  return;
@@ -77210,11 +77857,63 @@ var useOperationsOverviewRefresh = ({
77210
77857
  window.removeEventListener("pageshow", handlePageShow);
77211
77858
  window.removeEventListener("pagehide", handlePageHide);
77212
77859
  };
77213
- }, [companyId, getIsPageActive, isLiveScope, lineIds.length, refreshFromResume, startPolling, stopPolling, supabase]);
77860
+ }, [companyId, enabled, getIsPageActive, isLiveScope, lineIds.length, refreshFromResume, startPolling, stopPolling, supabase]);
77861
+ };
77862
+ var parseTimeToMinutes3 = (value) => {
77863
+ if (!value) return null;
77864
+ const parts = value.split(":");
77865
+ if (parts.length < 2) return null;
77866
+ const hours = Number(parts[0]);
77867
+ const minutes = Number(parts[1]);
77868
+ if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return null;
77869
+ if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;
77870
+ return hours * 60 + minutes;
77871
+ };
77872
+ var normalizeShiftId = (value) => {
77873
+ if (typeof value === "number" && Number.isFinite(value)) {
77874
+ return value;
77875
+ }
77876
+ if (typeof value === "string" && value.trim().length > 0) {
77877
+ const parsed = Number(value);
77878
+ return Number.isFinite(parsed) ? parsed : null;
77879
+ }
77880
+ return null;
77881
+ };
77882
+ var classifyShiftBucket = ({
77883
+ shiftName,
77884
+ shiftId,
77885
+ startTime,
77886
+ endTime
77887
+ }) => {
77888
+ const normalizedName = (shiftName || "").trim().toLowerCase();
77889
+ if (normalizedName) {
77890
+ if (["night", "graveyard", "evening"].some((keyword) => normalizedName.includes(keyword))) {
77891
+ return "night";
77892
+ }
77893
+ if (["day", "morning"].some((keyword) => normalizedName.includes(keyword))) {
77894
+ return "day";
77895
+ }
77896
+ }
77897
+ const startMinutes = parseTimeToMinutes3(startTime);
77898
+ const endMinutes = parseTimeToMinutes3(endTime);
77899
+ if (startMinutes !== null) {
77900
+ if (startMinutes >= 4 * 60 && startMinutes < 18 * 60) return "day";
77901
+ return "night";
77902
+ }
77903
+ if (endMinutes !== null) {
77904
+ if (endMinutes >= 6 * 60 && endMinutes <= 21 * 60) return "day";
77905
+ return "night";
77906
+ }
77907
+ const normalizedShiftId = normalizeShiftId(shiftId);
77908
+ if (normalizedShiftId === 0) return "day";
77909
+ if (normalizedShiftId === 1) return "night";
77910
+ return null;
77214
77911
  };
77215
77912
  var PlantHeadView = () => {
77216
77913
  const supabase = useSupabase();
77217
77914
  const entityConfig = useEntityConfig();
77915
+ const factoryViewId = entityConfig.factoryViewId || "factory";
77916
+ const staticShiftConfig = useShiftConfig();
77218
77917
  const appTimezone = useAppTimezone() || "UTC";
77219
77918
  const { navigate } = useNavigation();
77220
77919
  const { accessibleLineIds } = useUserLineAccess();
@@ -77222,11 +77921,21 @@ var PlantHeadView = () => {
77222
77921
  useHideMobileHeader(!!mobileMenuContext);
77223
77922
  const storeRef = React141__namespace.default.useRef(createOperationsOverviewStore());
77224
77923
  const store = storeRef.current;
77225
- const [dateRange, setDateRange] = React141__namespace.default.useState(() => getCurrentWeekToDateRange(appTimezone));
77226
- const [usesThisWeekComparison, setUsesThisWeekComparison] = React141__namespace.default.useState(true);
77924
+ const fallbackOperationalDate = React141__namespace.default.useMemo(
77925
+ () => getOperationalDate(appTimezone),
77926
+ [appTimezone]
77927
+ );
77928
+ const [dateRange, setDateRange] = React141__namespace.default.useState(() => ({
77929
+ startKey: fallbackOperationalDate,
77930
+ endKey: fallbackOperationalDate
77931
+ }));
77932
+ const [usesThisWeekComparison, setUsesThisWeekComparison] = React141__namespace.default.useState(false);
77227
77933
  const [trendMode, setTrendMode] = React141__namespace.default.useState("all");
77228
77934
  const [selectedSupervisorId, setSelectedSupervisorId] = React141__namespace.default.useState("all");
77229
77935
  const [selectedLineIds, setSelectedLineIds] = React141__namespace.default.useState([]);
77936
+ const [isInitialScopeReady, setIsInitialScopeReady] = React141__namespace.default.useState(false);
77937
+ const hasAutoInitializedScopeRef = React141__namespace.default.useRef(false);
77938
+ const hasUserAdjustedScopeRef = React141__namespace.default.useRef(false);
77230
77939
  React141__namespace.default.useEffect(() => {
77231
77940
  trackCorePageView("Operations Overview", {
77232
77941
  dashboard_surface: "operations_overview"
@@ -77248,8 +77957,10 @@ var PlantHeadView = () => {
77248
77957
  return dateRange;
77249
77958
  }, [currentWeekDisplayRange, dateRange, isCurrentWeekToDateRange, usesThisWeekComparison]);
77250
77959
  const normalizedLineIds = React141__namespace.default.useMemo(
77251
- () => Array.from(new Set((accessibleLineIds || []).filter(Boolean))).sort(),
77252
- [accessibleLineIds]
77960
+ () => Array.from(new Set(
77961
+ (accessibleLineIds || []).filter(Boolean).filter((lineId) => lineId !== factoryViewId)
77962
+ )).sort(),
77963
+ [accessibleLineIds, factoryViewId]
77253
77964
  );
77254
77965
  const lineIdsKey = React141__namespace.default.useMemo(
77255
77966
  () => normalizedLineIds.join(","),
@@ -77323,14 +78034,81 @@ var PlantHeadView = () => {
77323
78034
  () => selectedLineIds.length > 0 ? selectedLineIds : normalizedLineIds,
77324
78035
  [normalizedLineIds, selectedLineIds]
77325
78036
  );
78037
+ const {
78038
+ shiftConfigMap,
78039
+ isLoading: isShiftConfigLoading
78040
+ } = useMultiLineShiftConfigs(scopedLineIds, staticShiftConfig);
78041
+ const { shiftGroups, hasComputed: hasComputedShiftGroups } = useShiftGroups({
78042
+ enabled: scopedLineIds.length > 0 && !isShiftConfigLoading,
78043
+ shiftConfigMap,
78044
+ timezone: appTimezone
78045
+ });
78046
+ const currentShiftScope = React141__namespace.default.useMemo(() => {
78047
+ if (shiftGroups.length !== 1) return null;
78048
+ const group = shiftGroups[0];
78049
+ const referenceLineId = group.lineIds[0];
78050
+ const referenceShiftConfig = referenceLineId ? shiftConfigMap.get(referenceLineId) : null;
78051
+ const normalizedGroupShiftId = normalizeShiftId(group.shiftId);
78052
+ const shiftDefinition = referenceShiftConfig?.shifts?.find((shift) => normalizeShiftId(shift.shiftId) === normalizedGroupShiftId);
78053
+ const resolvedMode = classifyShiftBucket({
78054
+ shiftName: group.shiftName,
78055
+ shiftId: normalizedGroupShiftId,
78056
+ startTime: shiftDefinition?.startTime,
78057
+ endTime: shiftDefinition?.endTime
78058
+ });
78059
+ if (!resolvedMode || resolvedMode === "all") return null;
78060
+ return {
78061
+ date: group.date,
78062
+ trendMode: resolvedMode,
78063
+ shiftName: shiftDefinition?.shiftName || group.shiftName || null
78064
+ };
78065
+ }, [shiftConfigMap, shiftGroups]);
78066
+ const isShiftScopeResolved = React141__namespace.default.useMemo(
78067
+ () => !isShiftConfigLoading && hasComputedShiftGroups,
78068
+ [hasComputedShiftGroups, isShiftConfigLoading]
78069
+ );
77326
78070
  const initializedTimezoneRef = React141__namespace.default.useRef(appTimezone);
77327
78071
  React141__namespace.default.useEffect(() => {
77328
78072
  if (initializedTimezoneRef.current === appTimezone) return;
77329
- setDateRange(getCurrentWeekToDateRange(appTimezone));
77330
- setUsesThisWeekComparison(true);
78073
+ hasAutoInitializedScopeRef.current = false;
78074
+ hasUserAdjustedScopeRef.current = false;
78075
+ setDateRange({
78076
+ startKey: fallbackOperationalDate,
78077
+ endKey: fallbackOperationalDate
78078
+ });
78079
+ setTrendMode("all");
78080
+ setUsesThisWeekComparison(false);
78081
+ setIsInitialScopeReady(false);
77331
78082
  initializedTimezoneRef.current = appTimezone;
77332
- }, [appTimezone]);
78083
+ }, [appTimezone, fallbackOperationalDate]);
78084
+ React141__namespace.default.useEffect(() => {
78085
+ if (hasAutoInitializedScopeRef.current || hasUserAdjustedScopeRef.current) {
78086
+ return;
78087
+ }
78088
+ if (scopedLineIds.length === 0) {
78089
+ return;
78090
+ }
78091
+ if (!isShiftScopeResolved) {
78092
+ return;
78093
+ }
78094
+ setDateRange((previous) => {
78095
+ const nextStartKey = currentShiftScope?.date || fallbackOperationalDate;
78096
+ if (previous.startKey === nextStartKey && previous.endKey === nextStartKey) {
78097
+ return previous;
78098
+ }
78099
+ return {
78100
+ startKey: nextStartKey,
78101
+ endKey: nextStartKey
78102
+ };
78103
+ });
78104
+ setTrendMode(currentShiftScope?.trendMode || "all");
78105
+ setUsesThisWeekComparison(false);
78106
+ hasAutoInitializedScopeRef.current = true;
78107
+ setIsInitialScopeReady(true);
78108
+ }, [currentShiftScope, fallbackOperationalDate, isShiftScopeResolved, scopedLineIds.length]);
77333
78109
  const handleDateRangeChange = React141__namespace.default.useCallback((range, meta) => {
78110
+ hasUserAdjustedScopeRef.current = true;
78111
+ setIsInitialScopeReady(true);
77334
78112
  trackCoreEvent("Operations Overview Date Range Changed", {
77335
78113
  start_date: range.startKey,
77336
78114
  end_date: range.endKey
@@ -77347,6 +78125,8 @@ var PlantHeadView = () => {
77347
78125
  });
77348
78126
  }, []);
77349
78127
  const handleTrendModeChange = React141__namespace.default.useCallback((mode) => {
78128
+ hasUserAdjustedScopeRef.current = true;
78129
+ setIsInitialScopeReady(true);
77350
78130
  setTrendMode(mode);
77351
78131
  }, []);
77352
78132
  const handleSelectedLineIdsChange = React141__namespace.default.useCallback((lineIds) => {
@@ -77373,15 +78153,6 @@ var PlantHeadView = () => {
77373
78153
  params.set("rangeEnd", dateRange.endKey);
77374
78154
  return `/kpis/${lineId}?${params.toString()}`;
77375
78155
  }, [dateRange.endKey, dateRange.startKey]);
77376
- const handleOpenLineMonthlyHistory = React141__namespace.default.useCallback((lineId, lineName) => {
77377
- trackCoreEvent("Operations Overview Line Clicked", {
77378
- line_id: lineId,
77379
- line_name: lineName,
77380
- range_start: dateRange.startKey,
77381
- range_end: dateRange.endKey
77382
- });
77383
- navigate(buildLineMonthlyHistoryUrl(lineId));
77384
- }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, navigate]);
77385
78156
  const handleViewAllPoorestPerformers = React141__namespace.default.useCallback(() => {
77386
78157
  trackCoreEvent("Operations Overview View All Clicked", { section: "poorest_performers" });
77387
78158
  navigate("/kpis?tab=leaderboard");
@@ -77407,16 +78178,74 @@ var PlantHeadView = () => {
77407
78178
  }
77408
78179
  return void 0;
77409
78180
  }, [isCurrentWeekToDateRange, usesThisWeekComparison]);
78181
+ const effectiveDateRange = React141__namespace.default.useMemo(() => {
78182
+ if (isInitialScopeReady) {
78183
+ return dateRange;
78184
+ }
78185
+ const nextStartKey = currentShiftScope?.date || fallbackOperationalDate;
78186
+ return {
78187
+ startKey: nextStartKey,
78188
+ endKey: nextStartKey
78189
+ };
78190
+ }, [currentShiftScope, dateRange, fallbackOperationalDate, isInitialScopeReady]);
78191
+ const effectiveTrendMode = React141__namespace.default.useMemo(
78192
+ () => isInitialScopeReady ? trendMode : currentShiftScope?.trendMode || "all",
78193
+ [currentShiftScope, isInitialScopeReady, trendMode]
78194
+ );
78195
+ const hourlyLabelStartTime = React141__namespace.default.useMemo(() => {
78196
+ if (effectiveTrendMode === "all" || scopedLineIds.length === 0) {
78197
+ return null;
78198
+ }
78199
+ const shiftStartTimes = /* @__PURE__ */ new Set();
78200
+ scopedLineIds.forEach((lineId) => {
78201
+ const shiftConfig = shiftConfigMap.get(lineId);
78202
+ const matchingShift = shiftConfig?.shifts?.find((shift) => classifyShiftBucket({
78203
+ shiftName: shift.shiftName,
78204
+ shiftId: shift.shiftId,
78205
+ startTime: shift.startTime,
78206
+ endTime: shift.endTime
78207
+ }) === effectiveTrendMode);
78208
+ if (matchingShift?.startTime) {
78209
+ shiftStartTimes.add(matchingShift.startTime);
78210
+ }
78211
+ });
78212
+ if (shiftStartTimes.size !== 1) {
78213
+ return null;
78214
+ }
78215
+ return Array.from(shiftStartTimes)[0] || null;
78216
+ }, [effectiveTrendMode, scopedLineIds, shiftConfigMap]);
78217
+ const isSingleDayShiftScope = React141__namespace.default.useMemo(
78218
+ () => effectiveDateRange.startKey === effectiveDateRange.endKey && effectiveTrendMode !== "all",
78219
+ [effectiveDateRange.endKey, effectiveDateRange.startKey, effectiveTrendMode]
78220
+ );
78221
+ const isLiveScope = React141__namespace.default.useMemo(
78222
+ () => isSingleDayShiftScope && currentShiftScope !== null && effectiveDateRange.startKey === currentShiftScope.date && effectiveTrendMode === currentShiftScope.trendMode,
78223
+ [currentShiftScope, effectiveDateRange.startKey, effectiveTrendMode, isSingleDayShiftScope]
78224
+ );
78225
+ const handleOpenLineDetails = React141__namespace.default.useCallback((lineId, lineName) => {
78226
+ trackCoreEvent("Operations Overview Line Clicked", {
78227
+ line_id: lineId,
78228
+ line_name: lineName,
78229
+ range_start: dateRange.startKey,
78230
+ range_end: dateRange.endKey
78231
+ });
78232
+ if (isLiveScope) {
78233
+ navigate(`/kpis/${lineId}?returnTo=${encodeURIComponent("/")}`);
78234
+ return;
78235
+ }
78236
+ navigate(buildLineMonthlyHistoryUrl(lineId));
78237
+ }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, isLiveScope, navigate]);
77410
78238
  useOperationsOverviewRefresh({
77411
78239
  store,
77412
78240
  supabase,
77413
78241
  companyId: entityConfig.companyId,
77414
78242
  lineIds: scopedLineIds,
77415
- startKey: dateRange.startKey,
77416
- endKey: dateRange.endKey,
77417
- trendMode,
78243
+ startKey: effectiveDateRange.startKey,
78244
+ endKey: effectiveDateRange.endKey,
78245
+ trendMode: effectiveTrendMode,
77418
78246
  comparisonStrategy,
77419
- isLiveScope: isCurrentWeekToDateRange
78247
+ isLiveScope,
78248
+ enabled: scopedLineIds.length > 0 && isShiftScopeResolved
77420
78249
  });
77421
78250
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-h-screen bg-slate-50 w-full font-sans", children: [
77422
78251
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -77425,6 +78254,8 @@ var PlantHeadView = () => {
77425
78254
  dateRange,
77426
78255
  displayDateRange: headerDateRange,
77427
78256
  trendMode,
78257
+ isLiveScope,
78258
+ liveShiftName: currentShiftScope?.shiftName || null,
77428
78259
  lineOptions,
77429
78260
  supervisorOptions,
77430
78261
  selectedSupervisorId,
@@ -77447,7 +78278,7 @@ var PlantHeadView = () => {
77447
78278
  store,
77448
78279
  supervisorsByLineId,
77449
78280
  onViewAll: handleViewAllPoorestPerformers,
77450
- onLineClick: handleOpenLineMonthlyHistory
78281
+ onLineClick: handleOpenLineDetails
77451
78282
  }
77452
78283
  ),
77453
78284
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -77464,7 +78295,8 @@ var PlantHeadView = () => {
77464
78295
  {
77465
78296
  store,
77466
78297
  dateRange,
77467
- appTimezone
78298
+ appTimezone,
78299
+ hourlyLabelStartTime
77468
78300
  }
77469
78301
  ),
77470
78302
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -78325,6 +79157,7 @@ exports.isEfficiencyOnTrack = isEfficiencyOnTrack;
78325
79157
  exports.isFactoryScopedRole = isFactoryScopedRole;
78326
79158
  exports.isFullMonthRange = isFullMonthRange;
78327
79159
  exports.isLegacyConfiguration = isLegacyConfiguration;
79160
+ exports.isLoopbackHostname = isLoopbackHostname;
78328
79161
  exports.isPrefetchError = isPrefetchError;
78329
79162
  exports.isRecentFlowVideoGridMetricMode = isRecentFlowVideoGridMetricMode;
78330
79163
  exports.isSafari = isSafari;
@@ -78366,6 +79199,7 @@ exports.resetSubscriptionManager = resetSubscriptionManager;
78366
79199
  exports.s3VideoPreloader = s3VideoPreloader;
78367
79200
  exports.setSentryUserContext = setSentryUserContext;
78368
79201
  exports.setSentryWorkspaceContext = setSentryWorkspaceContext;
79202
+ exports.shouldEnableLocalDevTestLogin = shouldEnableLocalDevTestLogin;
78369
79203
  exports.shuffleArray = shuffleArray;
78370
79204
  exports.simulateApiDelay = simulateApiDelay;
78371
79205
  exports.skuService = skuService;