@optifye/dashboard-core 6.11.15 → 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
@@ -12864,6 +12864,7 @@ var useShiftGroups = ({
12864
12864
  }) => {
12865
12865
  const [shiftGroups, setShiftGroups] = React141.useState([]);
12866
12866
  const [shiftGroupsKey, setShiftGroupsKey] = React141.useState("");
12867
+ const [hasComputed, setHasComputed] = React141.useState(false);
12867
12868
  const lastKeyRef = React141.useRef("");
12868
12869
  const mapRef = React141.useRef(shiftConfigMap);
12869
12870
  React141.useEffect(() => {
@@ -12874,6 +12875,7 @@ var useShiftGroups = ({
12874
12875
  lastKeyRef.current = "";
12875
12876
  setShiftGroups([]);
12876
12877
  setShiftGroupsKey("");
12878
+ setHasComputed(false);
12877
12879
  return;
12878
12880
  }
12879
12881
  let isMounted = true;
@@ -12886,6 +12888,7 @@ var useShiftGroups = ({
12886
12888
  setShiftGroups(groups);
12887
12889
  setShiftGroupsKey(key);
12888
12890
  }
12891
+ setHasComputed(true);
12889
12892
  };
12890
12893
  compute();
12891
12894
  const intervalId = setInterval(compute, pollIntervalMs);
@@ -12894,7 +12897,7 @@ var useShiftGroups = ({
12894
12897
  clearInterval(intervalId);
12895
12898
  };
12896
12899
  }, [enabled, shiftConfigMap.size, timezone, pollIntervalMs]);
12897
- return { shiftGroups, shiftGroupsKey };
12900
+ return { shiftGroups, shiftGroupsKey, hasComputed };
12898
12901
  };
12899
12902
 
12900
12903
  // src/lib/types/efficiencyLegend.ts
@@ -12956,52 +12959,6 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
12956
12959
  }
12957
12960
  }
12958
12961
 
12959
- // src/lib/hooks/useDashboardMetrics.recentFlow.ts
12960
- var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
12961
- var getWorkspaceRecentFlowCacheKey = (workspace) => {
12962
- const workspaceKey = workspace.workspace_uuid || workspace.workspace_name || "unknown";
12963
- return `${workspaceKey}|${workspace.date}|${workspace.shift_id}`;
12964
- };
12965
- var toRecentFlowSnapshot = (workspace) => {
12966
- if (!isFiniteNumber(workspace.recent_flow_percent)) {
12967
- return null;
12968
- }
12969
- return {
12970
- recent_flow_percent: workspace.recent_flow_percent,
12971
- recent_flow_actual_rate_pph: workspace.recent_flow_actual_rate_pph ?? null,
12972
- recent_flow_healthy_rate_pph: workspace.recent_flow_healthy_rate_pph ?? null,
12973
- recent_flow_window_minutes: workspace.recent_flow_window_minutes ?? null,
12974
- recent_flow_effective_end_at: workspace.recent_flow_effective_end_at ?? null
12975
- };
12976
- };
12977
- var mergeWorkspaceRecentFlowMetrics = (workspaces, previousCache) => {
12978
- const nextCache = /* @__PURE__ */ new Map();
12979
- const mergedWorkspaces = workspaces.map((workspace) => {
12980
- const cacheKey = getWorkspaceRecentFlowCacheKey(workspace);
12981
- const currentSnapshot = toRecentFlowSnapshot(workspace);
12982
- if (workspace.recent_flow_mode === "computed" && currentSnapshot) {
12983
- nextCache.set(cacheKey, currentSnapshot);
12984
- return workspace;
12985
- }
12986
- if (workspace.recent_flow_mode === "hold") {
12987
- const cachedSnapshot = previousCache.get(cacheKey);
12988
- if (cachedSnapshot) {
12989
- nextCache.set(cacheKey, cachedSnapshot);
12990
- return {
12991
- ...workspace,
12992
- ...cachedSnapshot,
12993
- recent_flow_mode: "hold"
12994
- };
12995
- }
12996
- }
12997
- return workspace;
12998
- });
12999
- return {
13000
- workspaces: mergedWorkspaces,
13001
- cache: nextCache
13002
- };
13003
- };
13004
-
13005
12962
  // src/lib/hooks/useDashboardMetrics.ts
13006
12963
  var DEBUG_DASHBOARD_LOGS = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
13007
12964
  var logDebug = (...args) => {
@@ -13090,7 +13047,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13090
13047
  const abortControllerRef = React141.useRef(null);
13091
13048
  const lastFetchKeyRef = React141.useRef(null);
13092
13049
  const inFlightFetchKeyRef = React141.useRef(null);
13093
- const recentFlowCacheRef = React141.useRef(/* @__PURE__ */ new Map());
13094
13050
  const updateQueueRef = React141.useRef(false);
13095
13051
  const onLineMetricsUpdateRef = React141.useRef(onLineMetricsUpdate);
13096
13052
  const shiftGroupsRef = React141.useRef(shiftGroups);
@@ -13124,7 +13080,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13124
13080
  setError(null);
13125
13081
  lastFetchKeyRef.current = null;
13126
13082
  inFlightFetchKeyRef.current = null;
13127
- recentFlowCacheRef.current = /* @__PURE__ */ new Map();
13128
13083
  }, [lineId]);
13129
13084
  const fetchAllMetrics = React141.useCallback(async (options = {}) => {
13130
13085
  const { force = false } = options;
@@ -13399,12 +13354,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13399
13354
  actionType: item.action_type,
13400
13355
  actionName: item.action_name
13401
13356
  }),
13402
- recent_flow_mode: item.recent_flow_mode ?? void 0,
13403
13357
  recent_flow_percent: item.recent_flow_percent ?? null,
13404
- recent_flow_actual_rate_pph: item.recent_flow_actual_rate_pph ?? null,
13405
- recent_flow_healthy_rate_pph: item.recent_flow_healthy_rate_pph ?? null,
13406
- recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
13407
13358
  recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
13359
+ recent_flow_computed_at: item.recent_flow_computed_at ?? null,
13408
13360
  incoming_wip_current: item.incoming_wip_current ?? null,
13409
13361
  incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
13410
13362
  incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null
@@ -13415,16 +13367,11 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13415
13367
  const wsNumB = parseInt(b.workspace_name?.replace(/[^0-9]/g, "") || "0");
13416
13368
  return wsNumA - wsNumB;
13417
13369
  });
13418
- const {
13419
- workspaces: mergedWorkspaceData,
13420
- cache: nextRecentFlowCache
13421
- } = mergeWorkspaceRecentFlowMetrics(transformedWorkspaceData, recentFlowCacheRef.current);
13422
- recentFlowCacheRef.current = nextRecentFlowCache;
13423
- mergedWorkspaceData.forEach((metric) => {
13370
+ transformedWorkspaceData.forEach((metric) => {
13424
13371
  workspaceMetricsStore.setOverview(metric);
13425
13372
  });
13426
13373
  const newMetricsState = {
13427
- workspaceMetrics: mergedWorkspaceData,
13374
+ workspaceMetrics: transformedWorkspaceData,
13428
13375
  lineMetrics: allLineMetrics || [],
13429
13376
  metadata: { hasFlowBuffers, idleTimeVlmByLine },
13430
13377
  efficiencyLegend: efficiencyLegend ?? DEFAULT_EFFICIENCY_LEGEND
@@ -16849,7 +16796,7 @@ var useActiveBreaks = (lineIds) => {
16849
16796
  const [isLoading, setIsLoading] = React141.useState(true);
16850
16797
  const [error, setError] = React141.useState(null);
16851
16798
  const supabase = useSupabase();
16852
- const parseTimeToMinutes3 = (timeStr) => {
16799
+ const parseTimeToMinutes4 = (timeStr) => {
16853
16800
  const [hours, minutes] = timeStr.split(":").map(Number);
16854
16801
  return hours * 60 + minutes;
16855
16802
  };
@@ -16858,8 +16805,8 @@ var useActiveBreaks = (lineIds) => {
16858
16805
  return now4.getHours() * 60 + now4.getMinutes();
16859
16806
  };
16860
16807
  const isTimeInBreak = (breakStart, breakEnd, currentMinutes) => {
16861
- const startMinutes = parseTimeToMinutes3(breakStart);
16862
- const endMinutes = parseTimeToMinutes3(breakEnd);
16808
+ const startMinutes = parseTimeToMinutes4(breakStart);
16809
+ const endMinutes = parseTimeToMinutes4(breakEnd);
16863
16810
  if (endMinutes < startMinutes) {
16864
16811
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
16865
16812
  } else {
@@ -16867,8 +16814,8 @@ var useActiveBreaks = (lineIds) => {
16867
16814
  }
16868
16815
  };
16869
16816
  const calculateBreakProgress = (breakStart, breakEnd, currentMinutes) => {
16870
- const startMinutes = parseTimeToMinutes3(breakStart);
16871
- const endMinutes = parseTimeToMinutes3(breakEnd);
16817
+ const startMinutes = parseTimeToMinutes4(breakStart);
16818
+ const endMinutes = parseTimeToMinutes4(breakEnd);
16872
16819
  let elapsedMinutes = 0;
16873
16820
  let remainingMinutes = 0;
16874
16821
  if (endMinutes < startMinutes) {
@@ -16886,8 +16833,8 @@ var useActiveBreaks = (lineIds) => {
16886
16833
  return { elapsedMinutes, remainingMinutes };
16887
16834
  };
16888
16835
  const isTimeInShift = (startTime, endTime, currentMinutes) => {
16889
- const startMinutes = parseTimeToMinutes3(startTime);
16890
- const endMinutes = parseTimeToMinutes3(endTime);
16836
+ const startMinutes = parseTimeToMinutes4(startTime);
16837
+ const endMinutes = parseTimeToMinutes4(endTime);
16891
16838
  if (endMinutes < startMinutes) {
16892
16839
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
16893
16840
  } else {
@@ -16947,8 +16894,8 @@ var useActiveBreaks = (lineIds) => {
16947
16894
  const endTime = breakItem.end || breakItem.endTime || "00:00";
16948
16895
  let duration = breakItem.duration || 0;
16949
16896
  if (!duration || duration === 0) {
16950
- const startMinutes = parseTimeToMinutes3(startTime);
16951
- const endMinutes = parseTimeToMinutes3(endTime);
16897
+ const startMinutes = parseTimeToMinutes4(startTime);
16898
+ const endMinutes = parseTimeToMinutes4(endTime);
16952
16899
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
16953
16900
  }
16954
16901
  return {
@@ -16964,8 +16911,8 @@ var useActiveBreaks = (lineIds) => {
16964
16911
  const endTime = breakItem.end || breakItem.endTime || "00:00";
16965
16912
  let duration = breakItem.duration || 0;
16966
16913
  if (!duration || duration === 0) {
16967
- const startMinutes = parseTimeToMinutes3(startTime);
16968
- const endMinutes = parseTimeToMinutes3(endTime);
16914
+ const startMinutes = parseTimeToMinutes4(startTime);
16915
+ const endMinutes = parseTimeToMinutes4(endTime);
16969
16916
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
16970
16917
  }
16971
16918
  return {
@@ -23046,6 +22993,16 @@ var createThrottledReload = (interval = 5e3, maxReloads = 3) => {
23046
22993
  };
23047
22994
  var throttledReloadDashboard = createThrottledReload(5e3, 3);
23048
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
+
23049
23006
  // src/lib/utils/index.ts
23050
23007
  var formatIdleTime = (idleTimeInSeconds) => {
23051
23008
  if (!idleTimeInSeconds || idleTimeInSeconds <= 0) {
@@ -31230,12 +31187,16 @@ var LoginPage = ({
31230
31187
  onRateLimitCheck,
31231
31188
  logoSrc = optifye_logo_default,
31232
31189
  logoAlt = "Optifye",
31233
- brandName = "Optifye"
31190
+ brandName = "Optifye",
31191
+ showDevTestLogin = false,
31192
+ devTestLoginLabel = "Sign in as Test User",
31193
+ onDevTestLogin
31234
31194
  }) => {
31235
31195
  const [email, setEmail] = React141.useState("");
31236
31196
  const [otp, setOtp] = React141.useState("");
31237
31197
  const [step, setStep] = React141.useState("email");
31238
31198
  const [loading, setLoading] = React141.useState(false);
31199
+ const [devLoginLoading, setDevLoginLoading] = React141.useState(false);
31239
31200
  const [error, setError] = React141.useState(null);
31240
31201
  const [countdown, setCountdown] = React141.useState(0);
31241
31202
  const supabase = useSupabase();
@@ -31299,6 +31260,25 @@ var LoginPage = ({
31299
31260
  startCountdown();
31300
31261
  }
31301
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
+ };
31302
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: [
31303
31283
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl shadow-xl border border-slate-200 p-8", children: [
31304
31284
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8", children: [
@@ -31339,7 +31319,7 @@ var LoginPage = ({
31339
31319
  "button",
31340
31320
  {
31341
31321
  type: "submit",
31342
- disabled: loading,
31322
+ disabled: loading || devLoginLoading,
31343
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",
31344
31324
  children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31345
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: [
@@ -31349,7 +31329,31 @@ var LoginPage = ({
31349
31329
  "Sending..."
31350
31330
  ] }) : "Continue with Email"
31351
31331
  }
31352
- )
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
31353
31357
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "space-y-6", onSubmit: handleVerifyOTP, children: [
31354
31358
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
31355
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" }),
@@ -31374,7 +31378,7 @@ var LoginPage = ({
31374
31378
  "button",
31375
31379
  {
31376
31380
  type: "submit",
31377
- disabled: loading || otp.length !== 6,
31381
+ disabled: loading || devLoginLoading || otp.length !== 6,
31378
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",
31379
31383
  children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31380
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: [
@@ -32215,31 +32219,35 @@ var LineChartComponent = ({
32215
32219
  ...restOfChartProps
32216
32220
  }) => {
32217
32221
  const containerRef = React141__namespace.default.useRef(null);
32218
- const [containerReady, setContainerReady] = React141__namespace.default.useState(false);
32219
- const themeConfig = useThemeConfig();
32220
- const { formatNumber } = useFormatNumber();
32222
+ const [dimensions, setDimensions] = React141__namespace.default.useState({ width: 0, height: 0 });
32223
+ const [hasValidData, setHasValidData] = React141__namespace.default.useState(false);
32221
32224
  React141__namespace.default.useEffect(() => {
32222
- const checkContainerDimensions = () => {
32223
- if (containerRef.current) {
32224
- const rect = containerRef.current.getBoundingClientRect();
32225
- if (rect.width > 0 && rect.height > 0) {
32226
- setContainerReady(true);
32227
- }
32228
- }
32229
- };
32230
- checkContainerDimensions();
32231
- const resizeObserver = new ResizeObserver(checkContainerDimensions);
32232
- if (containerRef.current) {
32233
- 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);
32234
32233
  }
32235
- const fallbackTimeout = setTimeout(() => {
32236
- setContainerReady(true);
32237
- }, 100);
32238
- return () => {
32239
- resizeObserver.disconnect();
32240
- clearTimeout(fallbackTimeout);
32241
- };
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();
32242
32248
  }, []);
32249
+ const themeConfig = useThemeConfig();
32250
+ const { formatNumber } = useFormatNumber();
32243
32251
  const yAxisTickFormatter = (value) => {
32244
32252
  return `${formatNumber(value)}${yAxisUnit || ""}`;
32245
32253
  };
@@ -32261,57 +32269,71 @@ var LineChartComponent = ({
32261
32269
  const gridStrokeColor = themeConfig?.gray?.["300"] || "#ccc";
32262
32270
  const axisTickFillColor = themeConfig?.gray?.["600"] || "#666";
32263
32271
  const axisStrokeColor = themeConfig?.gray?.["400"] || "#999";
32264
- const chartContent = /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data, margin: { top: 5, right: 30, left: 20, bottom: 20 }, ...restOfChartProps, children: [
32265
- showGrid && /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStrokeColor }),
32266
- /* @__PURE__ */ jsxRuntime.jsx(
32267
- recharts.XAxis,
32268
- {
32269
- dataKey: xAxisDataKey,
32270
- label: xAxisLabel ? { value: xAxisLabel, position: "insideBottom", offset: -10 } : void 0,
32271
- tickFormatter: xAxisTickFormatter,
32272
- tick: { fontSize: 12, fill: axisTickFillColor },
32273
- stroke: axisStrokeColor
32274
- }
32275
- ),
32276
- /* @__PURE__ */ jsxRuntime.jsx(
32277
- recharts.YAxis,
32278
- {
32279
- label: yAxisLabel ? { value: yAxisLabel, angle: -90, position: "insideLeft" } : void 0,
32280
- tickFormatter: yAxisTickFormatter,
32281
- domain: yAxisDomain,
32282
- tick: { fontSize: 12, fill: axisTickFillColor },
32283
- stroke: axisStrokeColor
32284
- }
32285
- ),
32286
- showTooltip && /* @__PURE__ */ jsxRuntime.jsx(
32287
- recharts.Tooltip,
32288
- {
32289
- formatter: tooltipFormatter || defaultTooltipFormatter,
32290
- labelFormatter: tooltipLabelFormatter,
32291
- itemStyle: { color: "#111827" },
32292
- cursor: { strokeDasharray: "3 3" }
32293
- }
32294
- ),
32295
- showLegend && /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { payload: legendPayload }),
32296
- lines.map((lineConfig, index) => {
32297
- const lineProps = {
32298
- ...lineConfig,
32299
- key: lineConfig.dataKey,
32300
- type: lineConfig.type || "monotone",
32301
- stroke: lineConfig.stroke || defaultColors[index % defaultColors.length],
32302
- activeDot: lineConfig.activeDot !== void 0 ? lineConfig.activeDot : { r: 6 }
32303
- };
32304
- return /* @__PURE__ */ jsxRuntime.jsx(recharts.Line, { ...lineProps, children: lineConfig.labelList && /* @__PURE__ */ jsxRuntime.jsx(
32305
- recharts.LabelList,
32306
- {
32307
- dataKey: lineConfig.dataKey,
32308
- position: "top",
32309
- formatter: (value) => formatNumber(value),
32310
- ...typeof lineConfig.labelList === "object" ? lineConfig.labelList : {}
32311
- }
32312
- ) });
32313
- })
32314
- ] });
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
+ );
32315
32337
  if (responsive) {
32316
32338
  return /* @__PURE__ */ jsxRuntime.jsx(
32317
32339
  "div",
@@ -32319,11 +32341,20 @@ var LineChartComponent = ({
32319
32341
  ref: containerRef,
32320
32342
  className: clsx(fillContainer ? "w-full h-full" : "w-full h-auto", className),
32321
32343
  style: fillContainer ? { height: "100%", minHeight: "50px", minWidth: "100px" } : { aspectRatio: `${aspect}/1`, minHeight: "50px", minWidth: "100px" },
32322
- 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
+ )
32323
32354
  }
32324
32355
  );
32325
32356
  }
32326
- 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) });
32327
32358
  };
32328
32359
  var LineChart = React141__namespace.default.memo(LineChartComponent, (prevProps, nextProps) => {
32329
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)) {
@@ -32591,14 +32622,35 @@ var CycleTimeOverTimeChart = ({
32591
32622
  }) => {
32592
32623
  const MAX_DATA_POINTS = 40;
32593
32624
  const containerRef = React141__namespace.default.useRef(null);
32594
- const [containerReady, setContainerReady] = React141__namespace.default.useState(false);
32595
- 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) => {
32596
32648
  const [hours, minutes] = value.split(":").map(Number);
32597
32649
  if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return 0;
32598
32650
  return hours * 60 + minutes;
32599
32651
  };
32600
32652
  const formatHourLabel = (slotIndex) => {
32601
- const baseMinutes = parseTimeToMinutes3(shiftStart);
32653
+ const baseMinutes = parseTimeToMinutes4(shiftStart);
32602
32654
  const absoluteMinutes = baseMinutes + slotIndex * 60;
32603
32655
  const hour24 = Math.floor(absoluteMinutes % (24 * 60) / 60);
32604
32656
  const ampm = hour24 >= 12 ? "PM" : "AM";
@@ -32617,52 +32669,7 @@ var CycleTimeOverTimeChart = ({
32617
32669
  const displayData = getDisplayData(data);
32618
32670
  const DURATION = displayData.length;
32619
32671
  const effectiveDatasetKey = datasetKey || `cycle-time:${xAxisMode}`;
32620
- const [animatedDatasetKey, setAnimatedDatasetKey] = React141__namespace.default.useState(null);
32621
- const shouldAnimate = animatedDatasetKey !== effectiveDatasetKey;
32622
- const handleAnimationEnd = React141__namespace.default.useCallback(() => {
32623
- setAnimatedDatasetKey((currentValue) => currentValue === effectiveDatasetKey ? currentValue : effectiveDatasetKey);
32624
- }, [effectiveDatasetKey]);
32625
32672
  const finalData = displayData;
32626
- React141__namespace.default.useEffect(() => {
32627
- const containerNode = containerRef.current;
32628
- if (!containerNode) {
32629
- setContainerReady(true);
32630
- return void 0;
32631
- }
32632
- let frameId = null;
32633
- let resizeObserver = null;
32634
- const checkContainerDimensions = () => {
32635
- const rect = containerNode.getBoundingClientRect();
32636
- const isReady = rect.width > 0 && rect.height > 0;
32637
- if (isReady) {
32638
- setContainerReady(true);
32639
- }
32640
- return isReady;
32641
- };
32642
- if (checkContainerDimensions()) {
32643
- return void 0;
32644
- }
32645
- frameId = window.requestAnimationFrame(() => {
32646
- checkContainerDimensions();
32647
- });
32648
- if (typeof ResizeObserver !== "undefined") {
32649
- resizeObserver = new ResizeObserver(() => {
32650
- if (checkContainerDimensions() && resizeObserver) {
32651
- resizeObserver.disconnect();
32652
- resizeObserver = null;
32653
- }
32654
- });
32655
- resizeObserver.observe(containerNode);
32656
- } else {
32657
- setContainerReady(true);
32658
- }
32659
- return () => {
32660
- if (frameId !== null) {
32661
- window.cancelAnimationFrame(frameId);
32662
- }
32663
- resizeObserver?.disconnect();
32664
- };
32665
- }, []);
32666
32673
  const labelInterval = React141__namespace.default.useMemo(() => {
32667
32674
  if (xAxisMode === "hourly") {
32668
32675
  return Math.max(1, Math.ceil(DURATION / 8));
@@ -32870,144 +32877,154 @@ var CycleTimeOverTimeChart = ({
32870
32877
  return /* @__PURE__ */ jsxRuntime.jsxs(
32871
32878
  "div",
32872
32879
  {
32873
- ref: containerRef,
32874
32880
  className: `w-full h-full min-w-0 flex flex-col relative pb-2 ${className}`,
32875
32881
  style: { minHeight: "200px", minWidth: 0 },
32876
32882
  children: [
32877
32883
  renderLegend(),
32878
- /* @__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(
32879
- recharts.LineChart,
32884
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 w-full", ref: containerRef, children: /* @__PURE__ */ jsxRuntime.jsx(
32885
+ motion.div,
32880
32886
  {
32881
- data: chartData,
32882
- margin: {
32883
- top: 5,
32884
- right: 30,
32885
- bottom: 25,
32886
- left: 10
32887
- },
32888
- children: [
32889
- /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
32890
- /* @__PURE__ */ jsxRuntime.jsx(
32891
- recharts.XAxis,
32892
- {
32893
- dataKey: "label",
32894
- tick: { fontSize: 11 },
32895
- interval: 0,
32896
- angle: xAxisMode === "hourly" ? 0 : -30,
32897
- textAnchor: xAxisMode === "hourly" ? "middle" : "end",
32898
- tickMargin: xAxisMode === "hourly" ? 8 : 15,
32899
- height: xAxisMode === "hourly" ? 40 : 60
32900
- }
32901
- ),
32902
- /* @__PURE__ */ jsxRuntime.jsx(
32903
- recharts.YAxis,
32904
- {
32905
- tickMargin: 8,
32906
- width: 45,
32907
- yAxisId: "cycle",
32908
- domain: ["auto", "auto"],
32909
- ticks: [0, idealCycleTime, ...Array.from({ length: 4 }, (_, i) => (i + 1) * Math.ceil(idealCycleTime / 2))].sort((a, b) => a - b),
32910
- tickFormatter: (value) => String(value),
32911
- tick: (props) => {
32912
- const { x, y, payload } = props;
32913
- const displayValue = typeof payload.value === "number" ? payload.value.toFixed(1) : String(payload.value);
32914
- return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
32915
- "text",
32916
- {
32917
- x: 0,
32918
- y: 0,
32919
- dy: 4,
32920
- textAnchor: "end",
32921
- fill: payload.value === idealCycleTime ? "#E34329" : "#666",
32922
- fontSize: 12,
32923
- fontWeight: payload.value === idealCycleTime ? "bold" : "normal",
32924
- children: displayValue
32925
- },
32926
- `tick-${payload.value}-${x}-${y}`
32927
- ) });
32928
- }
32929
- }
32930
- ),
32931
- showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
32932
- recharts.YAxis,
32933
- {
32934
- yAxisId: "idle",
32935
- orientation: "right",
32936
- tickMargin: 8,
32937
- width: 35,
32938
- domain: [0, 60],
32939
- tickFormatter: (value) => `${value}m`,
32940
- tick: { fontSize: 11, fill: "#f59e0b" },
32941
- axisLine: false,
32942
- tickLine: false
32943
- }
32944
- ),
32945
- /* @__PURE__ */ jsxRuntime.jsx(
32946
- recharts.Tooltip,
32947
- {
32948
- cursor: { stroke: "#E5E7EB", strokeWidth: 1 },
32949
- content: renderChartTooltip,
32950
- animationDuration: 200
32951
- }
32952
- ),
32953
- /* @__PURE__ */ jsxRuntime.jsx(
32954
- recharts.ReferenceLine,
32955
- {
32956
- y: idealCycleTime,
32957
- yAxisId: "cycle",
32958
- stroke: "#E34329",
32959
- strokeDasharray: "3 3",
32960
- strokeWidth: 2,
32961
- label: {
32962
- position: "right",
32963
- value: `${idealCycleTime.toFixed(1)}s`,
32964
- fill: "#E34329",
32965
- fontSize: 12,
32966
- fontWeight: 500
32967
- }
32968
- }
32969
- ),
32970
- /* @__PURE__ */ jsxRuntime.jsx(
32971
- recharts.Line,
32972
- {
32973
- type: "monotone",
32974
- yAxisId: "cycle",
32975
- dataKey: "cycleTime",
32976
- stroke: "#3B82F6",
32977
- strokeWidth: 2,
32978
- connectNulls: false,
32979
- dot: renderCycleDot,
32980
- activeDot: renderCycleActiveDot,
32981
- isAnimationActive: shouldAnimate,
32982
- animationBegin: 0,
32983
- animationDuration: 1200,
32984
- animationEasing: "ease-out",
32985
- onAnimationEnd: handleAnimationEnd
32986
- },
32987
- `${effectiveDatasetKey}:cycle`
32988
- ),
32989
- showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
32990
- recharts.Line,
32991
- {
32992
- type: "monotone",
32993
- yAxisId: "idle",
32994
- dataKey: "idleMinutes",
32995
- stroke: "#f59e0b",
32996
- strokeWidth: 2,
32997
- strokeDasharray: "4 4",
32998
- connectNulls: false,
32999
- dot: renderIdleDot,
33000
- activeDot: renderIdleActiveDot,
33001
- isAnimationActive: shouldAnimate,
33002
- animationBegin: 0,
33003
- animationDuration: 1200,
33004
- animationEasing: "ease-out"
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
33005
32902
  },
33006
- `${effectiveDatasetKey}:idle`
33007
- )
33008
- ]
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
32915
+ }
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
+ ) });
32943
+ }
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
32982
+ }
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
+ )
33009
33026
  }
33010
- ) }) : /* @__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
+ ) })
33011
33028
  ]
33012
33029
  }
33013
33030
  );
@@ -33857,10 +33874,10 @@ var HourlyOutputChart = React141__namespace.default.memo(HourlyOutputChartCompon
33857
33874
  HourlyOutputChart.displayName = "HourlyOutputChart";
33858
33875
 
33859
33876
  // src/components/dashboard/grid/videoGridMetricUtils.ts
33860
- var VIDEO_GRID_LEGEND_LABEL = "Flow";
33877
+ var VIDEO_GRID_LEGEND_LABEL = "7 Minute Efficiency";
33861
33878
  var MAP_GRID_LEGEND_LABEL = "Efficiency";
33862
- var MIXED_VIDEO_GRID_LEGEND_LABEL = "Flow / Efficiency";
33863
- 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);
33864
33881
  var isVideoGridRecentFlowEnabled = (workspace) => isRecentFlowVideoGridMetricMode(
33865
33882
  workspace.video_grid_metric_mode,
33866
33883
  workspace.assembly_enabled === true
@@ -33869,11 +33886,11 @@ var isVideoGridWipGated = (workspace) => isWipGatedVideoGridMetricMode(
33869
33886
  workspace.video_grid_metric_mode,
33870
33887
  workspace.assembly_enabled === true
33871
33888
  );
33872
- var hasVideoGridRecentFlow = (workspace) => isVideoGridRecentFlowEnabled(workspace) && isFiniteNumber2(workspace.recent_flow_percent);
33889
+ var hasVideoGridRecentFlow = (workspace) => isVideoGridRecentFlowEnabled(workspace) && isFiniteNumber(workspace.recent_flow_percent);
33873
33890
  var isVideoGridRecentFlowUnavailable = (workspace) => isVideoGridRecentFlowEnabled(workspace) && !hasVideoGridRecentFlow(workspace);
33874
33891
  var getVideoGridMetricValue = (workspace) => {
33875
33892
  const recentFlowPercent = workspace.recent_flow_percent;
33876
- if (hasVideoGridRecentFlow(workspace) && isFiniteNumber2(recentFlowPercent)) {
33893
+ if (hasVideoGridRecentFlow(workspace) && isFiniteNumber(recentFlowPercent)) {
33877
33894
  return recentFlowPercent;
33878
33895
  }
33879
33896
  if (isVideoGridRecentFlowUnavailable(workspace)) {
@@ -33884,7 +33901,7 @@ var getVideoGridMetricValue = (workspace) => {
33884
33901
  var hasIncomingWipMapping = (workspace) => Boolean(workspace.incoming_wip_buffer_name);
33885
33902
  var getVideoGridBaseColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33886
33903
  const metricValue = getVideoGridMetricValue(workspace);
33887
- if (!isFiniteNumber2(metricValue)) {
33904
+ if (!isFiniteNumber(metricValue)) {
33888
33905
  return "neutral";
33889
33906
  }
33890
33907
  return getEfficiencyColor(metricValue, legend);
@@ -33899,7 +33916,7 @@ var isLowWipGreenOverride = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33899
33916
  if (!hasIncomingWipMapping(workspace)) {
33900
33917
  return false;
33901
33918
  }
33902
- return isFiniteNumber2(workspace.incoming_wip_current) && workspace.incoming_wip_current <= 1;
33919
+ return isFiniteNumber(workspace.incoming_wip_current) && workspace.incoming_wip_current <= 1;
33903
33920
  };
33904
33921
  var toMinuteBucket = (minuteBucket) => Number.isFinite(minuteBucket) ? Math.floor(minuteBucket) : Math.floor(Date.now() / 6e4);
33905
33922
  var getEffectiveFlowMinuteBucket = (workspace) => {
@@ -33941,7 +33958,7 @@ var getVideoGridColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) =>
33941
33958
  if (!hasIncomingWipMapping(workspace)) {
33942
33959
  return baseColor;
33943
33960
  }
33944
- if (!isFiniteNumber2(workspace.incoming_wip_current)) {
33961
+ if (!isFiniteNumber(workspace.incoming_wip_current)) {
33945
33962
  return "neutral";
33946
33963
  }
33947
33964
  if (isLowWipGreenOverride(workspace, legend)) {
@@ -34133,7 +34150,7 @@ var VideoCard = React141__namespace.default.memo(({
34133
34150
  }
34134
34151
  );
34135
34152
  }, (prevProps, nextProps) => {
34136
- 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) {
34137
34154
  return false;
34138
34155
  }
34139
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) {
@@ -44601,7 +44618,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44601
44618
  return null;
44602
44619
  }
44603
44620
  };
44604
- const getShiftIcon = (shift) => {
44621
+ const getShiftIcon2 = (shift) => {
44605
44622
  if (shift === "Day") {
44606
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" }) });
44607
44624
  } else {
@@ -44627,7 +44644,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44627
44644
  }
44628
44645
  if (variant === "enhanced") {
44629
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: [
44630
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon(currentShiftText) }),
44647
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon2(currentShiftText) }),
44631
44648
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-base font-medium text-blue-800", children: [
44632
44649
  currentShiftText,
44633
44650
  " Shift"
@@ -44635,7 +44652,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44635
44652
  ] });
44636
44653
  }
44637
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: [
44638
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon(currentShiftText) }),
44655
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon2(currentShiftText) }),
44639
44656
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-base font-medium text-blue-800", children: [
44640
44657
  currentShiftText,
44641
44658
  " Shift"
@@ -47272,7 +47289,7 @@ var LinePdfGenerator = ({
47272
47289
  doc.setLineWidth(0.8);
47273
47290
  doc.line(20, 123, 190, 123);
47274
47291
  const hourlyOverviewStartY = 128;
47275
- const parseTimeToMinutes3 = (timeStr) => {
47292
+ const parseTimeToMinutes4 = (timeStr) => {
47276
47293
  const [hours, minutes] = timeStr.split(":");
47277
47294
  const hour = parseInt(hours, 10);
47278
47295
  const minute = parseInt(minutes || "0", 10);
@@ -47305,7 +47322,7 @@ var LinePdfGenerator = ({
47305
47322
  };
47306
47323
  };
47307
47324
  const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
47308
- const startMinutes = parseTimeToMinutes3(startTimeStr);
47325
+ const startMinutes = parseTimeToMinutes4(startTimeStr);
47309
47326
  if (Number.isNaN(startMinutes)) {
47310
47327
  return [];
47311
47328
  }
@@ -47313,7 +47330,7 @@ var LinePdfGenerator = ({
47313
47330
  const defaultHours = 11;
47314
47331
  return Array.from({ length: defaultHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
47315
47332
  }
47316
- const endMinutes = parseTimeToMinutes3(endTimeStr);
47333
+ const endMinutes = parseTimeToMinutes4(endTimeStr);
47317
47334
  if (Number.isNaN(endMinutes)) {
47318
47335
  const fallbackHours = 11;
47319
47336
  return Array.from({ length: fallbackHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
@@ -49082,7 +49099,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
49082
49099
  minute: "2-digit",
49083
49100
  hour12: true
49084
49101
  });
49085
- const parseTimeToMinutes3 = (timeValue) => {
49102
+ const parseTimeToMinutes4 = (timeValue) => {
49086
49103
  const [hourPart, minutePart] = timeValue.split(":").map(Number);
49087
49104
  const hour = Number.isFinite(hourPart) ? hourPart : 0;
49088
49105
  const minute = Number.isFinite(minutePart) ? minutePart : 0;
@@ -49099,8 +49116,8 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
49099
49116
  const IST_OFFSET_MINUTES = 330;
49100
49117
  return Date.UTC(year, month - 1, day, hour, minute) - IST_OFFSET_MINUTES * 60 * 1e3;
49101
49118
  };
49102
- const shiftStartMinutes = parseTimeToMinutes3(workspace.shift_start);
49103
- const shiftEndMinutes = parseTimeToMinutes3(workspace.shift_end);
49119
+ const shiftStartMinutes = parseTimeToMinutes4(workspace.shift_start);
49120
+ const shiftEndMinutes = parseTimeToMinutes4(workspace.shift_end);
49104
49121
  const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
49105
49122
  const shiftStartUtcMs = toShiftUtcMs(workspace.date, workspace.shift_start);
49106
49123
  const shiftEndUtcMs = toShiftUtcMs(workspace.date, workspace.shift_end) + (wrapsMidnight ? 24 * 60 * 60 * 1e3 : 0);
@@ -51407,7 +51424,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51407
51424
  const rawName = currentShift.shiftName || "Day";
51408
51425
  return rawName.toLowerCase().includes("shift") ? rawName : `${rawName} Shift`;
51409
51426
  };
51410
- const getShiftIcon = () => {
51427
+ const getShiftIcon2 = () => {
51411
51428
  const currentShift = getCurrentShift(timezone, shiftConfig);
51412
51429
  const shiftName = (currentShift.shiftName || "").toLowerCase();
51413
51430
  if (shiftName.includes("day") || shiftName.includes("morning") || currentShift.shiftId === 0) {
@@ -51441,7 +51458,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51441
51458
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: /* @__PURE__ */ jsxRuntime.jsx(Timer2, {}) }),
51442
51459
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-300", children: "|" }),
51443
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: [
51444
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75", children: getShiftIcon() }),
51461
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75", children: getShiftIcon2() }),
51445
51462
  getShiftName()
51446
51463
  ] })
51447
51464
  ] })
@@ -51458,7 +51475,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51458
51475
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 inline-flex flex-wrap items-center gap-3", children: [
51459
51476
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs md:text-sm font-medium text-gray-600 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx(Timer2, {}) }),
51460
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: [
51461
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon() }),
51478
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon2() }),
51462
51479
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs md:text-sm font-medium text-gray-600 whitespace-nowrap", children: getShiftName() })
51463
51480
  ] }) })
51464
51481
  ] })
@@ -58429,7 +58446,7 @@ var FactoryView = ({
58429
58446
  const currentShift = getCurrentShiftInfo();
58430
58447
  return (currentShift.shiftName || "Day").replace(/ Shift$/i, "");
58431
58448
  };
58432
- const getShiftIcon = () => {
58449
+ const getShiftIcon2 = () => {
58433
58450
  const currentShift = getCurrentShiftInfo();
58434
58451
  const shiftNameLower = (currentShift.shiftName || "").toLowerCase();
58435
58452
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || currentShift.shiftId === 0) {
@@ -58475,7 +58492,7 @@ var FactoryView = ({
58475
58492
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center justify-center gap-2", children: [
58476
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, {}) }) }),
58477
58494
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
58478
- /* @__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() }),
58479
58496
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-gray-700", children: [
58480
58497
  getShiftName(),
58481
58498
  " Shift"
@@ -58494,7 +58511,7 @@ var FactoryView = ({
58494
58511
  " IST"
58495
58512
  ] }),
58496
58513
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1", children: [
58497
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon() }),
58514
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon2() }),
58498
58515
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
58499
58516
  getShiftName(),
58500
58517
  " Shift"
@@ -61206,7 +61223,7 @@ var KPIDetailView = ({
61206
61223
  const getShiftName = React141.useCallback((shiftId) => {
61207
61224
  return getShiftNameById(shiftId, configuredTimezone, shiftConfig);
61208
61225
  }, [configuredTimezone, shiftConfig]);
61209
- const getShiftIcon = React141.useCallback((shiftId) => {
61226
+ const getShiftIcon2 = React141.useCallback((shiftId) => {
61210
61227
  if (shiftId === 0) {
61211
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" }) });
61212
61229
  }
@@ -62086,7 +62103,7 @@ var KPIDetailView = ({
62086
62103
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
62087
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)) }) }),
62088
62105
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
62089
- /* @__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) }),
62090
62107
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-gray-700", children: [
62091
62108
  getShiftName(chartMetrics.shift_id ?? 0).replace(/ Shift$/i, ""),
62092
62109
  " Shift"
@@ -62112,7 +62129,7 @@ var KPIDetailView = ({
62112
62129
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
62113
62130
  ] }),
62114
62131
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
62115
- /* @__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) }),
62116
62133
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
62117
62134
  getShiftName(chartMetrics.shift_id ?? 0).replace(/ Shift$/i, ""),
62118
62135
  " Shift"
@@ -62130,7 +62147,7 @@ var KPIDetailView = ({
62130
62147
  return `${startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
62131
62148
  })() }) }),
62132
62149
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
62133
- /* @__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) }),
62134
62151
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: getShiftName(selectedShiftId) })
62135
62152
  ] })
62136
62153
  ] }),
@@ -62147,7 +62164,7 @@ var KPIDetailView = ({
62147
62164
  ] }),
62148
62165
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
62149
62166
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
62150
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(selectedShiftId) }),
62167
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(selectedShiftId) }),
62151
62168
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
62152
62169
  getShiftName(selectedShiftId).replace(/ Shift$/i, ""),
62153
62170
  " Shift"
@@ -63523,7 +63540,7 @@ var KPIsOverviewView = ({
63523
63540
  const headerShiftId = showHistoricalLeaderboardHeader ? effectiveLeaderboardShiftId : currentShiftDetails.shiftId;
63524
63541
  const headerShiftName = getShiftNameById(headerShiftId, configuredTimezone, shiftConfig).replace(/ Shift$/i, "");
63525
63542
  const headerDateLabel = isMonthlyMode ? getMonthRange() : formatLocalDate2(headerDateKey);
63526
- const getShiftIcon = (shiftId) => {
63543
+ const getShiftIcon2 = (shiftId) => {
63527
63544
  const shiftNameLower = getShiftNameById(shiftId, configuredTimezone, shiftConfig).toLowerCase();
63528
63545
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || shiftId === 0) {
63529
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" }) });
@@ -63640,7 +63657,7 @@ var KPIsOverviewView = ({
63640
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 }) }),
63641
63658
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63642
63659
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
63643
- /* @__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) }),
63644
63661
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: headerShiftName })
63645
63662
  ] }),
63646
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: [
@@ -63836,7 +63853,7 @@ var KPIsOverviewView = ({
63836
63853
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63837
63854
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
63838
63855
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
63839
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon(headerShiftId) }),
63856
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(headerShiftId) }),
63840
63857
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
63841
63858
  headerShiftName,
63842
63859
  " Shift"
@@ -63980,6 +63997,52 @@ var KPIsOverviewView = ({
63980
63997
  ] });
63981
63998
  };
63982
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";
63983
64046
  var IsolatedTimer = React141.memo(() => {
63984
64047
  return /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {});
63985
64048
  });
@@ -63997,11 +64060,11 @@ var HeaderRibbon = React141.memo(({
63997
64060
  currentDate,
63998
64061
  currentMobileDate,
63999
64062
  shiftId,
64000
- getShiftIcon,
64063
+ getShiftIcon: getShiftIcon2,
64001
64064
  getShiftName,
64002
64065
  showTimer = true
64003
64066
  }) => {
64004
- const shiftIcon = React141.useMemo(() => getShiftIcon(shiftId), [getShiftIcon, shiftId]);
64067
+ const shiftIcon = React141.useMemo(() => getShiftIcon2(shiftId), [getShiftIcon2, shiftId]);
64005
64068
  const shiftName = React141.useMemo(() => getShiftName(shiftId), [getShiftName, shiftId]);
64006
64069
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
64007
64070
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
@@ -64043,8 +64106,9 @@ var MobileWorkspaceCard = React141.memo(({
64043
64106
  isClickable,
64044
64107
  onWorkspaceClick,
64045
64108
  getMedalIcon,
64046
- efficiencyLabel
64047
- }) => /* @__PURE__ */ jsxRuntime.jsx(
64109
+ metricLabel,
64110
+ isAssemblyMode
64111
+ }) => /* @__PURE__ */ jsxRuntime.jsxs(
64048
64112
  motion.div,
64049
64113
  {
64050
64114
  layout: true,
@@ -64055,28 +64119,31 @@ var MobileWorkspaceCard = React141.memo(({
64055
64119
  },
64056
64120
  onClick: isClickable ? () => onWorkspaceClick(workspace, rank) : void 0,
64057
64121
  className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] ${isClickable ? "cursor-pointer" : "cursor-not-allowed opacity-75"}`,
64058
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
64059
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
64060
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
64061
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-700", children: [
64062
- "#",
64063
- 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)
64064
64131
  ] }),
64065
- 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
+ ] })
64066
64136
  ] }),
64067
- /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
64068
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children: workspace.displayName }),
64069
- /* @__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 })
64070
64140
  ] })
64071
64141
  ] }),
64072
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
64073
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-lg font-bold text-gray-900", children: /* @__PURE__ */ jsxRuntime.jsx(AnimatedEfficiency, { value: workspace.efficiency || 0 }) }),
64074
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: efficiencyLabel })
64075
- ] })
64076
- ] })
64142
+ isAssemblyMode && /* @__PURE__ */ jsxRuntime.jsx(CycleTimeComparison, { workspace, variant: "mobile" })
64143
+ ]
64077
64144
  }
64078
64145
  ), (prevProps, nextProps) => {
64079
- 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;
64080
64147
  });
64081
64148
  MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
64082
64149
  var DesktopWorkspaceRow = React141.memo(({
@@ -64085,7 +64152,8 @@ var DesktopWorkspaceRow = React141.memo(({
64085
64152
  rowClass,
64086
64153
  isClickable,
64087
64154
  onWorkspaceClick,
64088
- getMedalIcon
64155
+ getMedalIcon,
64156
+ isAssemblyMode
64089
64157
  }) => /* @__PURE__ */ jsxRuntime.jsxs(
64090
64158
  motion.tr,
64091
64159
  {
@@ -64102,11 +64170,11 @@ var DesktopWorkspaceRow = React141.memo(({
64102
64170
  ] }) }),
64103
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 }) }),
64104
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 }) }),
64105
- /* @__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 }) })
64106
64174
  ]
64107
64175
  }
64108
64176
  ), (prevProps, nextProps) => {
64109
- 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;
64110
64178
  });
64111
64179
  DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
64112
64180
  var LeaderboardDetailView = React141.memo(({
@@ -64128,6 +64196,7 @@ var LeaderboardDetailView = React141.memo(({
64128
64196
  const supabase = useSupabase();
64129
64197
  const [sortAscending, setSortAscending] = React141.useState(false);
64130
64198
  const [viewType, setViewType] = React141.useState("operator");
64199
+ const [outputCategory, setOutputCategory] = React141.useState("standard");
64131
64200
  const [activeTab, setActiveTab] = React141.useState("today");
64132
64201
  const timezone = useAppTimezone();
64133
64202
  const staticShiftConfig = useShiftConfig();
@@ -64206,6 +64275,7 @@ var LeaderboardDetailView = React141.memo(({
64206
64275
  const monthlyRequestKeyRef = React141.useRef(null);
64207
64276
  const leaderboardUpdateQueuedRef = React141.useRef(false);
64208
64277
  const leaderboardUpdateTimerRef = React141.useRef(null);
64278
+ const leaderboardViewTrackedRef = React141.useRef(null);
64209
64279
  const filterRef = React141.useRef(null);
64210
64280
  const filterButtonRef = React141.useRef(null);
64211
64281
  const mobileFilterButtonRef = React141.useRef(null);
@@ -64338,6 +64408,17 @@ var LeaderboardDetailView = React141.memo(({
64338
64408
  }
64339
64409
  return Array.from(uniqueLines.entries()).map(([id3, name]) => ({ id: id3, label: name }));
64340
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]);
64341
64422
  const shiftOptions = React141.useMemo(() => {
64342
64423
  if (activeShiftConfig?.shifts && activeShiftConfig.shifts.length > 0) {
64343
64424
  return activeShiftConfig.shifts.map((shift2) => ({
@@ -64361,6 +64442,33 @@ var LeaderboardDetailView = React141.memo(({
64361
64442
  const parsed = Number(selectedShiftFilter);
64362
64443
  return Number.isFinite(parsed) ? parsed : void 0;
64363
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]);
64364
64472
  const handleLineFilterChange = React141.useCallback((value) => {
64365
64473
  setSelectedLineFilter(value);
64366
64474
  }, []);
@@ -64370,17 +64478,19 @@ var LeaderboardDetailView = React141.memo(({
64370
64478
  const clearFilters = React141.useCallback(() => {
64371
64479
  setSortAscending(false);
64372
64480
  setSelectedLineFilter("all");
64481
+ setOutputCategory("standard");
64373
64482
  setSelectedShiftFilter(currentShiftInfo.shiftId.toString());
64374
64483
  }, [currentShiftInfo.shiftId]);
64375
64484
  const activeFiltersCount = React141.useMemo(() => {
64376
64485
  let count = 0;
64377
64486
  if (sortAscending) count++;
64378
64487
  if (selectedLineFilter !== "all") count++;
64488
+ if (showOutputCategoryDropdown && outputCategory !== "standard") count++;
64379
64489
  if (selectedShiftFilter !== currentShiftInfo.shiftId.toString()) {
64380
64490
  count++;
64381
64491
  }
64382
64492
  return count;
64383
- }, [sortAscending, selectedLineFilter, selectedShiftFilter, currentShiftInfo.shiftId]);
64493
+ }, [sortAscending, selectedLineFilter, outputCategory, showOutputCategoryDropdown, selectedShiftFilter, currentShiftInfo.shiftId]);
64384
64494
  const shouldFetchShiftConfigs = !date && shiftId === void 0;
64385
64495
  const {
64386
64496
  shiftConfigMap: multiLineShiftConfigMap,
@@ -64432,7 +64542,8 @@ var LeaderboardDetailView = React141.memo(({
64432
64542
  action_count: entry.total_output || 0,
64433
64543
  pph: entry.avg_pph || 0,
64434
64544
  performance_score: entry.performance_score ?? 0,
64435
- 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,
64436
64547
  trend: 0,
64437
64548
  predicted_output: 0,
64438
64549
  efficiency: entry.efficiency || 0,
@@ -64610,7 +64721,7 @@ var LeaderboardDetailView = React141.memo(({
64610
64721
  const currentShift = getCurrentShift(timezone || "Asia/Kolkata", activeShiftConfig);
64611
64722
  return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", activeShiftConfig);
64612
64723
  }, [timezone, activeShiftConfig, shiftGroups]);
64613
- const getShiftIcon = React141.useCallback((shiftId2) => {
64724
+ const getShiftIcon2 = React141.useCallback((shiftId2) => {
64614
64725
  if (shiftId2 === void 0 && shiftGroups.length > 1) {
64615
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" }) });
64616
64727
  }
@@ -64676,15 +64787,20 @@ var LeaderboardDetailView = React141.memo(({
64676
64787
  if (!canOpenWorkspace(workspace.line_id)) {
64677
64788
  return;
64678
64789
  }
64790
+ const cycleRatio = getCycleRatio(workspace);
64679
64791
  trackCoreEvent("Workspace from Leaderboard Clicked", {
64680
64792
  workspace_name: workspace.workspace_name,
64681
64793
  workspace_id: workspace.workspace_uuid,
64682
64794
  rank,
64683
64795
  total_workspaces: workspacesLengthRef.current,
64684
64796
  // Use ref instead of state to avoid dependency
64797
+ metric_context: viewType === "machine" ? "machine" : outputCategory,
64685
64798
  efficiency: workspace.efficiency,
64686
64799
  action_count: workspace.action_count,
64687
- 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
64688
64804
  });
64689
64805
  const displayName = workspace.displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
64690
64806
  const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName, workspace.line_id) : "";
@@ -64708,7 +64824,7 @@ var LeaderboardDetailView = React141.memo(({
64708
64824
  const combinedParams = navParams ? `${navParams}&${contextParamString}` : `?${contextParamString}`;
64709
64825
  navigation.navigate(`/workspace/${workspace.workspace_uuid}${combinedParams}`);
64710
64826
  }
64711
- }, [canOpenWorkspace, onWorkspaceClick, navigation, date, shiftId]);
64827
+ }, [canOpenWorkspace, onWorkspaceClick, navigation, date, shiftId, outputCategory, viewType]);
64712
64828
  React141.useEffect(() => {
64713
64829
  workspacesLengthRef.current = activeEntries.length || 0;
64714
64830
  }, [activeEntries.length]);
@@ -64729,16 +64845,20 @@ var LeaderboardDetailView = React141.memo(({
64729
64845
  return activeEntries.map((ws) => ({
64730
64846
  ...ws,
64731
64847
  displayName: ws.displayName || getWorkspaceDisplayName(ws.workspace_name, ws.line_id),
64732
- lineName: getLineName(ws.line_id)
64848
+ lineName: getLineName(ws.line_id),
64849
+ isAssemblyLine: scopedLineAssemblyMap.get(ws.line_id) === true
64733
64850
  }));
64734
- }, [activeEntries, getLineName]);
64851
+ }, [activeEntries, getLineName, scopedLineAssemblyMap]);
64735
64852
  const sortedWorkspaces = React141.useMemo(() => {
64736
64853
  let filtered = [...workspaceDisplayData];
64737
64854
  filtered = filtered.filter((ws) => {
64738
64855
  if (viewType === "machine") {
64739
64856
  return ws.monitoring_mode === "uptime";
64740
64857
  }
64741
- return ws.monitoring_mode !== "uptime";
64858
+ if (ws.monitoring_mode === "uptime") {
64859
+ return false;
64860
+ }
64861
+ return isAssemblyMode ? ws.isAssemblyLine : !ws.isAssemblyLine;
64742
64862
  });
64743
64863
  if (selectedLineFilter !== "all") {
64744
64864
  filtered = filtered.filter((ws) => ws.line_id === selectedLineFilter);
@@ -64747,13 +64867,61 @@ var LeaderboardDetailView = React141.memo(({
64747
64867
  filtered = filtered.filter((ws) => ws.shift_id?.toString() === selectedShiftFilter);
64748
64868
  }
64749
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
+ }
64750
64878
  const effA = a.efficiency || 0;
64751
64879
  const effB = b.efficiency || 0;
64752
64880
  return sortAscending ? effA - effB : effB - effA;
64753
64881
  });
64754
- }, [workspaceDisplayData, sortAscending, selectedLineFilter, selectedShiftFilter, activeTab, viewType]);
64882
+ }, [workspaceDisplayData, sortAscending, selectedLineFilter, selectedShiftFilter, activeTab, viewType, isAssemblyMode]);
64755
64883
  const loading = activeTab === "today" ? todayLoading : monthlyLoading;
64756
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
+ ]);
64757
64925
  const currentDateFormatted = React141.useMemo(() => {
64758
64926
  const dateStr = (/* @__PURE__ */ new Date()).toDateString();
64759
64927
  return formatDate2(new Date(dateStr));
@@ -64771,7 +64939,9 @@ var LeaderboardDetailView = React141.memo(({
64771
64939
  error.message
64772
64940
  ] }) });
64773
64941
  }
64774
- 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";
64775
64945
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, style: { willChange: "contents" }, children: [
64776
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: [
64777
64947
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
@@ -64821,7 +64991,7 @@ var LeaderboardDetailView = React141.memo(({
64821
64991
  ] }),
64822
64992
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
64823
64993
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
64824
- /* @__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 }),
64825
64995
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
64826
64996
  "select",
64827
64997
  {
@@ -64830,8 +65000,25 @@ var LeaderboardDetailView = React141.memo(({
64830
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",
64831
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` },
64832
65002
  children: [
64833
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "desc", children: "Highest to Lowest" }),
64834
- /* @__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" })
64835
65022
  ]
64836
65023
  }
64837
65024
  ) })
@@ -64873,7 +65060,7 @@ var LeaderboardDetailView = React141.memo(({
64873
65060
  currentDate: activeTab === "monthly" ? monthlyRangeText : currentDateFormatted,
64874
65061
  currentMobileDate: activeTab === "monthly" ? monthlyRangeText : currentMobileDateFormatted,
64875
65062
  shiftId: activeTab === "monthly" ? monthlyShiftId : todayShiftId,
64876
- getShiftIcon,
65063
+ getShiftIcon: getShiftIcon2,
64877
65064
  getShiftName,
64878
65065
  showTimer: activeTab === "today"
64879
65066
  }
@@ -64971,6 +65158,20 @@ var LeaderboardDetailView = React141.memo(({
64971
65158
  ]
64972
65159
  }
64973
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
+ ) }),
64974
65175
  /* @__PURE__ */ jsxRuntime.jsxs(
64975
65176
  "button",
64976
65177
  {
@@ -65002,7 +65203,8 @@ var LeaderboardDetailView = React141.memo(({
65002
65203
  isClickable: canOpenWorkspace(ws.line_id),
65003
65204
  onWorkspaceClick: stableHandleWorkspaceClick,
65004
65205
  getMedalIcon: stableGetMedalIcon,
65005
- efficiencyLabel
65206
+ metricLabel,
65207
+ isAssemblyMode
65006
65208
  },
65007
65209
  ws.workspace_uuid
65008
65210
  );
@@ -65013,7 +65215,7 @@ var LeaderboardDetailView = React141.memo(({
65013
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" }),
65014
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" }),
65015
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" }),
65016
- /* @__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 })
65017
65219
  ] }) }),
65018
65220
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-100", children: sortedWorkspaces.map((ws, index) => {
65019
65221
  const isTopThree = index < 3;
@@ -65026,7 +65228,8 @@ var LeaderboardDetailView = React141.memo(({
65026
65228
  rowClass,
65027
65229
  isClickable: canOpenWorkspace(ws.line_id),
65028
65230
  onWorkspaceClick: stableHandleWorkspaceClick,
65029
- getMedalIcon: stableGetMedalIcon
65231
+ getMedalIcon: stableGetMedalIcon,
65232
+ isAssemblyMode
65030
65233
  },
65031
65234
  ws.workspace_uuid
65032
65235
  );
@@ -65044,7 +65247,10 @@ function LoginView({
65044
65247
  logoSrc = optifye_logo_default,
65045
65248
  logoAlt = "Optifye",
65046
65249
  brandName = "Optifye",
65047
- onRateLimitCheck
65250
+ onRateLimitCheck,
65251
+ showDevTestLogin = false,
65252
+ devTestLoginLabel,
65253
+ onDevTestLogin
65048
65254
  }) {
65049
65255
  return /* @__PURE__ */ jsxRuntime.jsx(
65050
65256
  LoginPage,
@@ -65052,7 +65258,10 @@ function LoginView({
65052
65258
  logoSrc,
65053
65259
  logoAlt,
65054
65260
  brandName,
65055
- onRateLimitCheck
65261
+ onRateLimitCheck,
65262
+ showDevTestLogin,
65263
+ devTestLoginLabel,
65264
+ onDevTestLogin
65056
65265
  }
65057
65266
  );
65058
65267
  }
@@ -68240,20 +68449,30 @@ function useTimezone(options = {}) {
68240
68449
  }
68241
68450
 
68242
68451
  // src/views/workspace-detail-view.utils.ts
68243
- var getWorkspaceCycleTimePresentation = ({
68452
+ var getWorkspaceDetailLayoutMode = ({
68244
68453
  workspace,
68245
68454
  showCycleTimeChart
68246
68455
  }) => {
68247
68456
  if (workspace?.monitoring_mode === "uptime") {
68248
- return "output";
68457
+ return "uptime";
68249
68458
  }
68250
68459
  if (showCycleTimeChart === false) {
68251
68460
  return "output";
68252
68461
  }
68253
- if (!shouldUseAssemblyCycleTimeLayout(workspace)) {
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") {
68254
68470
  return "output";
68255
68471
  }
68256
- return workspace?.cycle_time_data_status === "missing_clips" ? "cycle_unavailable" : "cycle_chart";
68472
+ if (!authoritativeMetrics) {
68473
+ return "chart_loading";
68474
+ }
68475
+ return authoritativeMetrics.cycle_time_data_status === "missing_clips" ? "cycle_unavailable" : "cycle_chart";
68257
68476
  };
68258
68477
  var formatDateInTimezone = (date = /* @__PURE__ */ new Date(), timezone, options) => {
68259
68478
  const defaultOptions = {
@@ -68661,33 +68880,34 @@ var WorkspaceDetailView = ({
68661
68880
  idle_time_hourly: void 0
68662
68881
  };
68663
68882
  }, [cachedOverviewMetrics, shiftConfig?.shifts]);
68664
- const workspace = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics || overviewFallback;
68883
+ const authoritativeCycleMetrics = isHistoricView ? historicMetrics : liveMetrics;
68884
+ const workspace = authoritativeCycleMetrics || cachedDetailedMetrics || overviewFallback;
68665
68885
  const { timezone: cycleTimeTimezone } = useTimezone({
68666
68886
  lineId: effectiveLineId || workspace?.line_id || void 0,
68667
68887
  workspaceId: workspaceId || void 0
68668
68888
  });
68669
68889
  const effectiveCycleTimeTimezone = cycleTimeTimezone || timezone;
68670
- const detailedWorkspaceMetrics = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics;
68890
+ const detailedWorkspaceMetrics = authoritativeCycleMetrics || cachedDetailedMetrics;
68671
68891
  const cycleTimeChartData = React141.useMemo(
68672
- () => 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) => {
68673
68893
  const numericValue = Number(value);
68674
68894
  return Number.isFinite(numericValue) ? numericValue : 0;
68675
68895
  }) : [],
68676
- [workspace?.hourly_cycle_times]
68896
+ [authoritativeCycleMetrics?.hourly_cycle_times]
68677
68897
  );
68678
68898
  const maskedCycleTimeChartData = React141.useMemo(
68679
68899
  () => maskFutureHourlySeries({
68680
68900
  data: cycleTimeChartData,
68681
- shiftStart: workspace?.shift_start,
68682
- shiftEnd: workspace?.shift_end,
68683
- shiftDate: workspace?.date || date || calculatedOperationalDate || null,
68901
+ shiftStart: authoritativeCycleMetrics?.shift_start,
68902
+ shiftEnd: authoritativeCycleMetrics?.shift_end,
68903
+ shiftDate: authoritativeCycleMetrics?.date || date || calculatedOperationalDate || null,
68684
68904
  timezone: effectiveCycleTimeTimezone
68685
68905
  }),
68686
68906
  [
68687
68907
  cycleTimeChartData,
68688
- workspace?.shift_start,
68689
- workspace?.shift_end,
68690
- workspace?.date,
68908
+ authoritativeCycleMetrics?.shift_start,
68909
+ authoritativeCycleMetrics?.shift_end,
68910
+ authoritativeCycleMetrics?.date,
68691
68911
  date,
68692
68912
  calculatedOperationalDate,
68693
68913
  effectiveCycleTimeTimezone
@@ -68695,13 +68915,13 @@ var WorkspaceDetailView = ({
68695
68915
  );
68696
68916
  const cycleTimeDatasetKey = React141.useMemo(
68697
68917
  () => [
68698
- workspace?.workspace_id || workspaceId || "workspace",
68699
- date || workspace?.date || "live",
68700
- parsedShiftId ?? workspace?.shift_id ?? "current",
68918
+ authoritativeCycleMetrics?.workspace_id || workspaceId || "workspace",
68919
+ date || authoritativeCycleMetrics?.date || "live",
68920
+ parsedShiftId ?? authoritativeCycleMetrics?.shift_id ?? "current",
68701
68921
  "hourly",
68702
68922
  "backend"
68703
68923
  ].join(":"),
68704
- [workspace?.workspace_id, workspaceId, date, workspace?.date, parsedShiftId, workspace?.shift_id]
68924
+ [authoritativeCycleMetrics?.workspace_id, workspaceId, date, authoritativeCycleMetrics?.date, parsedShiftId, authoritativeCycleMetrics?.shift_id]
68705
68925
  );
68706
68926
  const hasWorkspaceSnapshot = Boolean(workspace);
68707
68927
  const loading = ((isHistoricView ? historicLoading : liveLoading) || isShiftConfigLoading) && !hasWorkspaceSnapshot;
@@ -68950,30 +69170,46 @@ var WorkspaceDetailView = ({
68950
69170
  action_type: workspace.action_type
68951
69171
  } : null;
68952
69172
  const isAssemblyWorkspace = shouldUseAssemblyCycleTimeLayout(workspaceCycleTimeEligibility);
68953
- const cycleTimePresentation = getWorkspaceCycleTimePresentation({
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({
68954
69185
  workspace: workspace ? {
68955
69186
  monitoring_mode: workspace.monitoring_mode,
68956
69187
  line_assembly_enabled: workspace.line_assembly_enabled,
68957
69188
  action_family: workspace.action_family,
68958
- action_type: workspace.action_type,
68959
- cycle_time_data_status: workspace.cycle_time_data_status
69189
+ action_type: workspace.action_type
69190
+ } : null,
69191
+ authoritativeMetrics: authoritativeCycleMetrics ? {
69192
+ cycle_time_data_status: authoritativeCycleMetrics.cycle_time_data_status
68960
69193
  } : null,
68961
69194
  showCycleTimeChart
68962
69195
  });
68963
- const shouldShowCycleTimeChart = cycleTimePresentation !== "output";
69196
+ const shouldShowCycleTimeChart = cycleTimePresentation === "cycle_chart";
68964
69197
  const shouldShowCycleTimeUnavailableState = cycleTimePresentation === "cycle_unavailable";
68965
- const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
69198
+ const shouldShowCycleTimeLoadingState = cycleTimePresentation === "chart_loading";
69199
+ const shouldShowAssemblyOverviewLoadingState = isAssemblyCycleLayout && shouldShowCycleTimeLoadingState && hasWorkspaceSnapshot;
69200
+ const showIdleBreakdownChart = !isAssemblyCycleLayout && idleTimeVlmEnabled;
69201
+ const canToggleChartIdleTime = !isUptimeMode && !shouldShowCycleTimeLoadingState && !shouldShowCycleTimeUnavailableState;
68966
69202
  const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
68967
69203
  const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
68968
69204
  const rawHourlyIdleMinutes = React141.useMemo(() => {
68969
69205
  if (!shouldShowCycleTimeChart || !workspace?.idle_time_hourly || !workspace?.shift_start) return [];
68970
- const parseTimeToMinutes3 = (time2) => {
69206
+ const parseTimeToMinutes4 = (time2) => {
68971
69207
  const [h, m] = time2.split(":").map(Number);
68972
69208
  if (!Number.isFinite(h) || !Number.isFinite(m)) return 0;
68973
69209
  return h * 60 + m;
68974
69210
  };
68975
- const startTotal = parseTimeToMinutes3(workspace.shift_start);
68976
- 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;
68977
69213
  const endTotal = endTotalRaw <= startTotal ? endTotalRaw + 24 * 60 : endTotalRaw;
68978
69214
  const shiftDuration = Math.max(60, endTotal - startTotal);
68979
69215
  const totalHourSlots = Math.max(1, Math.ceil(shiftDuration / 60));
@@ -69023,6 +69259,18 @@ var WorkspaceDetailView = ({
69023
69259
  workspace.cycle_completion_clip_count
69024
69260
  ] })
69025
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
+ ] }), []);
69026
69274
  const shiftDurationMinutes = React141.useMemo(
69027
69275
  () => getShiftDurationMinutes(workspace?.shift_start, workspace?.shift_end),
69028
69276
  [workspace?.shift_start, workspace?.shift_end]
@@ -69077,7 +69325,7 @@ var WorkspaceDetailView = ({
69077
69325
  }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time]);
69078
69326
  const overviewTabLabel = isUptimeMode ? "Utilization" : "Efficiency";
69079
69327
  const idleClipFetchEnabled = Boolean(
69080
- workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && !shouldShowCycleTimeChart && !isUptimeMode
69328
+ workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && isOutputLayout
69081
69329
  );
69082
69330
  const {
69083
69331
  idleClips: idleTimeClips,
@@ -69148,7 +69396,7 @@ var WorkspaceDetailView = ({
69148
69396
  }
69149
69397
  }
69150
69398
  };
69151
- const getShiftIcon = (shiftType) => {
69399
+ const getShiftIcon2 = (shiftType) => {
69152
69400
  const shiftTypeLower = shiftType?.toLowerCase() || "";
69153
69401
  if (shiftTypeLower.includes("day") || shiftTypeLower.includes("morning")) {
69154
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" }) });
@@ -69297,7 +69545,7 @@ var WorkspaceDetailView = ({
69297
69545
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: (() => {
69298
69546
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
69299
69547
  const shiftName = shift2?.shiftName || (selectedShift === 0 ? "Day Shift" : "Night Shift");
69300
- return getShiftIcon(shiftName);
69548
+ return getShiftIcon2(shiftName);
69301
69549
  })() }),
69302
69550
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: (() => {
69303
69551
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
@@ -69325,7 +69573,7 @@ var WorkspaceDetailView = ({
69325
69573
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: (() => {
69326
69574
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
69327
69575
  const shiftName = shift2?.shiftName || (selectedShift === 0 ? "Day Shift" : "Night Shift");
69328
- return getShiftIcon(shiftName);
69576
+ return getShiftIcon2(shiftName);
69329
69577
  })() }),
69330
69578
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: (() => {
69331
69579
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
@@ -69338,7 +69586,7 @@ var WorkspaceDetailView = ({
69338
69586
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
69339
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)) }) }),
69340
69588
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
69341
- /* @__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) }),
69342
69590
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: workspace.shift_type })
69343
69591
  ] }),
69344
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
@@ -69369,7 +69617,7 @@ var WorkspaceDetailView = ({
69369
69617
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
69370
69618
  ] }),
69371
69619
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-blue-600", children: [
69372
- /* @__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) }),
69373
69621
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
69374
69622
  workspace.shift_type.replace(/ Shift$/i, ""),
69375
69623
  " Shift"
@@ -69488,9 +69736,9 @@ var WorkspaceDetailView = ({
69488
69736
  ] })
69489
69737
  ] }),
69490
69738
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4", children: [
69491
- 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: [
69492
69740
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "block lg:hidden space-y-6 pb-6", children: [
69493
- !shouldShowCycleTimeChart && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69741
+ isOutputLayout && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69494
69742
  motion.div,
69495
69743
  {
69496
69744
  className: "bg-white rounded-lg shadow-sm p-6 h-[300px]",
@@ -69518,8 +69766,8 @@ var WorkspaceDetailView = ({
69518
69766
  animate: "animate",
69519
69767
  children: [
69520
69768
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-4", children: [
69521
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
69522
- !isUptimeMode && !shouldShowCycleTimeUnavailableState && /* @__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(
69523
69771
  "button",
69524
69772
  {
69525
69773
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69548,19 +69796,19 @@ var WorkspaceDetailView = ({
69548
69796
  timezone,
69549
69797
  elapsedMinutes: elapsedShiftMinutes
69550
69798
  }
69551
- ) : shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69799
+ ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69552
69800
  CycleTimeOverTimeChart,
69553
69801
  {
69554
69802
  data: maskedCycleTimeChartData,
69555
- idealCycleTime: workspace.ideal_cycle_time || 0,
69556
- shiftStart: workspace.shift_start || "",
69557
- shiftEnd: workspace.shift_end || "",
69803
+ idealCycleTime: authoritativeCycleMetrics?.ideal_cycle_time || 0,
69804
+ shiftStart: authoritativeCycleMetrics?.shift_start || "",
69805
+ shiftEnd: authoritativeCycleMetrics?.shift_end || "",
69558
69806
  xAxisMode: "hourly",
69559
69807
  datasetKey: cycleTimeDatasetKey,
69560
69808
  showIdleTime: showChartIdleTime,
69561
69809
  idleTimeData: hourlyIdleMinutes
69562
69810
  }
69563
- ) : /* @__PURE__ */ jsxRuntime.jsx(
69811
+ ) : null : /* @__PURE__ */ jsxRuntime.jsx(
69564
69812
  HourlyOutputChart2,
69565
69813
  {
69566
69814
  data: workspace.hourly_action_counts || [],
@@ -69580,7 +69828,7 @@ var WorkspaceDetailView = ({
69580
69828
  ]
69581
69829
  }
69582
69830
  ),
69583
- !shouldShowCycleTimeChart && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
69831
+ showIdleBreakdownChart && /* @__PURE__ */ jsxRuntime.jsxs(
69584
69832
  motion.div,
69585
69833
  {
69586
69834
  className: "bg-white rounded-lg shadow-sm p-4 h-[300px]",
@@ -69600,7 +69848,7 @@ var WorkspaceDetailView = ({
69600
69848
  ]
69601
69849
  }
69602
69850
  ),
69603
- isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData }) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69851
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData }) : isAssemblyCycleLayout ? /* @__PURE__ */ jsxRuntime.jsx(
69604
69852
  WorkspaceCycleTimeMetricCards,
69605
69853
  {
69606
69854
  workspace,
@@ -69620,7 +69868,7 @@ var WorkspaceDetailView = ({
69620
69868
  desktopTopSectionClass
69621
69869
  ),
69622
69870
  children: [
69623
- !shouldShowCycleTimeChart && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69871
+ isOutputLayout && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69624
69872
  motion.div,
69625
69873
  {
69626
69874
  className: "bg-white rounded-lg shadow-sm p-4 lg:col-span-2 flex flex-col min-h-0",
@@ -69644,15 +69892,15 @@ var WorkspaceDetailView = ({
69644
69892
  {
69645
69893
  className: clsx(
69646
69894
  "bg-white rounded-lg shadow-sm p-4 flex flex-col min-h-0",
69647
- 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"
69648
69896
  ),
69649
69897
  variants: chartCardVariants,
69650
69898
  initial: "initial",
69651
69899
  animate: "animate",
69652
69900
  children: [
69653
69901
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 mb-4 flex-none", children: [
69654
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : shouldShowCycleTimeChart ? "Cycle time trend" : "Hourly Output" }),
69655
- !isUptimeMode && !shouldShowCycleTimeUnavailableState && /* @__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(
69656
69904
  "button",
69657
69905
  {
69658
69906
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69677,19 +69925,19 @@ var WorkspaceDetailView = ({
69677
69925
  timezone,
69678
69926
  elapsedMinutes: elapsedShiftMinutes
69679
69927
  }
69680
- ) : shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69928
+ ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69681
69929
  CycleTimeOverTimeChart,
69682
69930
  {
69683
69931
  data: maskedCycleTimeChartData,
69684
- idealCycleTime: workspace.ideal_cycle_time || 0,
69685
- shiftStart: workspace.shift_start || "",
69686
- shiftEnd: workspace.shift_end || "",
69932
+ idealCycleTime: authoritativeCycleMetrics?.ideal_cycle_time || 0,
69933
+ shiftStart: authoritativeCycleMetrics?.shift_start || "",
69934
+ shiftEnd: authoritativeCycleMetrics?.shift_end || "",
69687
69935
  xAxisMode: "hourly",
69688
69936
  datasetKey: cycleTimeDatasetKey,
69689
69937
  showIdleTime: showChartIdleTime,
69690
69938
  idleTimeData: hourlyIdleMinutes
69691
69939
  }
69692
- ) : /* @__PURE__ */ jsxRuntime.jsx(
69940
+ ) : null : /* @__PURE__ */ jsxRuntime.jsx(
69693
69941
  HourlyOutputChart2,
69694
69942
  {
69695
69943
  data: workspace.hourly_action_counts || [],
@@ -69707,7 +69955,7 @@ var WorkspaceDetailView = ({
69707
69955
  ]
69708
69956
  }
69709
69957
  ),
69710
- !shouldShowCycleTimeChart && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
69958
+ showIdleBreakdownChart && /* @__PURE__ */ jsxRuntime.jsxs(
69711
69959
  motion.div,
69712
69960
  {
69713
69961
  className: clsx(
@@ -69733,7 +69981,7 @@ var WorkspaceDetailView = ({
69733
69981
  ]
69734
69982
  }
69735
69983
  ),
69736
- 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(
69737
69985
  WorkspaceCycleTimeMetricCards,
69738
69986
  {
69739
69987
  workspace,
@@ -69744,7 +69992,7 @@ var WorkspaceDetailView = ({
69744
69992
  }
69745
69993
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
69746
69994
  ] })
69747
- ] }),
69995
+ ] }) }),
69748
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: [
69749
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: [
69750
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" }) }),
@@ -72376,7 +72624,7 @@ var normalizeLabel = (value) => {
72376
72624
  const trimmed = value.trim();
72377
72625
  return trimmed.length > 0 ? trimmed : void 0;
72378
72626
  };
72379
- var toFiniteNumber = (value) => {
72627
+ var toFiniteNumber2 = (value) => {
72380
72628
  if (typeof value === "number" && Number.isFinite(value)) return value;
72381
72629
  if (typeof value === "string" && value.trim().length > 0) {
72382
72630
  const parsed = Number(value);
@@ -72410,7 +72658,7 @@ var formatImprovementPieceGain = (pcsGain) => {
72410
72658
  var getPositiveImprovementGain = ({
72411
72659
  estimated_gain_pieces
72412
72660
  }) => {
72413
- const issueGain = toFiniteNumber(estimated_gain_pieces);
72661
+ const issueGain = toFiniteNumber2(estimated_gain_pieces);
72414
72662
  return {
72415
72663
  pcsGain: issueGain !== null && issueGain > 0 ? issueGain : null
72416
72664
  };
@@ -72418,7 +72666,7 @@ var getPositiveImprovementGain = ({
72418
72666
  var getImprovementPcsGainSortValue = (input) => {
72419
72667
  const { pcsGain } = getPositiveImprovementGain(input);
72420
72668
  if (pcsGain !== null) return pcsGain;
72421
- const raw = toFiniteNumber(input.estimated_gain_pieces);
72669
+ const raw = toFiniteNumber2(input.estimated_gain_pieces);
72422
72670
  return raw ?? null;
72423
72671
  };
72424
72672
  var getImprovementDisplayMetadata = ({
@@ -72438,7 +72686,7 @@ var getImprovementDisplayMetadata = ({
72438
72686
  metadataLabel: [workstationLabel, lineLabel, supervisorLabel].join(" \xB7 ")
72439
72687
  };
72440
72688
  };
72441
- var toFiniteNumber2 = (value) => {
72689
+ var toFiniteNumber3 = (value) => {
72442
72690
  if (typeof value === "number" && Number.isFinite(value)) return value;
72443
72691
  if (typeof value === "string" && value.trim().length > 0) {
72444
72692
  const parsed = Number(value);
@@ -72466,15 +72714,15 @@ var compareImprovementRecommendationPriority = (left, right) => {
72466
72714
  if (leftIndustrial !== rightIndustrial) {
72467
72715
  return rightIndustrial - leftIndustrial;
72468
72716
  }
72469
- const leftGain = toFiniteNumber2(left.estimated_gain_pieces);
72470
- const rightGain = toFiniteNumber2(right.estimated_gain_pieces);
72717
+ const leftGain = toFiniteNumber3(left.estimated_gain_pieces);
72718
+ const rightGain = toFiniteNumber3(right.estimated_gain_pieces);
72471
72719
  if (leftGain !== rightGain) {
72472
72720
  if (leftGain === null) return 1;
72473
72721
  if (rightGain === null) return -1;
72474
72722
  return rightGain - leftGain;
72475
72723
  }
72476
- const leftRatio = toFiniteNumber2(left.gain_to_target_ratio);
72477
- 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);
72478
72726
  if (leftRatio !== rightRatio) {
72479
72727
  if (leftRatio === null) return 1;
72480
72728
  if (rightRatio === null) return -1;
@@ -75779,6 +76027,7 @@ var EMPTY_OVERVIEW_POOREST_LINES = {
75779
76027
  };
75780
76028
  var EMPTY_OVERVIEW_TREND = {
75781
76029
  shift_mode: "all",
76030
+ granularity: "day",
75782
76031
  points: []
75783
76032
  };
75784
76033
  var EMPTY_IDLE_BREAKDOWN = [];
@@ -75851,8 +76100,11 @@ var normalizePoorestLines = (value) => ({
75851
76100
  });
75852
76101
  var normalizeTrend = (value) => ({
75853
76102
  shift_mode: value?.shift_mode || "all",
76103
+ granularity: value?.granularity === "hour" ? "hour" : "day",
75854
76104
  points: (value?.points || []).map((point) => ({
75855
76105
  date: point?.date,
76106
+ label: point?.label,
76107
+ hour_index: normalizeNumber(point?.hour_index),
75856
76108
  avg_efficiency: normalizeNumber(point?.avg_efficiency)
75857
76109
  }))
75858
76110
  });
@@ -76194,17 +76446,25 @@ var formatSignedIdleDuration = (seconds) => {
76194
76446
  const sign = seconds > 0 ? "+" : "-";
76195
76447
  return `${sign}${formatIdleDuration(Math.abs(seconds))}`;
76196
76448
  };
76197
- var formatComparisonWindow = (dayCount, comparisonStrategy) => {
76449
+ var formatComparisonWindow = ({
76450
+ currentDayCount,
76451
+ previousDayCount,
76452
+ comparisonStrategy,
76453
+ shiftMode
76454
+ }) => {
76198
76455
  if (comparisonStrategy === "previous_full_week") return "last week";
76199
- if (!dayCount || !Number.isFinite(dayCount)) return "previous range";
76200
- 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"}`;
76201
76461
  };
76202
76462
  var buildDeltaBadge = (delta, options) => {
76203
76463
  if (delta === null || delta === void 0 || !Number.isFinite(delta)) {
76204
76464
  return {
76205
76465
  icon: null,
76206
- className: "bg-slate-100 text-slate-500",
76207
- text: `No ${options.comparisonLabel} baseline`
76466
+ className: "bg-slate-100 text-slate-400",
76467
+ text: "\u2014"
76208
76468
  };
76209
76469
  }
76210
76470
  const direction = delta >= 0 ? "up" : "down";
@@ -76215,6 +76475,25 @@ var buildDeltaBadge = (delta, options) => {
76215
76475
  text: `${options.formatter(delta)} vs ${options.comparisonLabel}`
76216
76476
  };
76217
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
+ };
76218
76497
  var buildLineDeltaTone = (delta, comparisonLabel) => {
76219
76498
  if (delta === null || delta === void 0 || !Number.isFinite(delta)) {
76220
76499
  return {
@@ -76270,6 +76549,8 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76270
76549
  dateRange,
76271
76550
  displayDateRange,
76272
76551
  trendMode,
76552
+ isLiveScope,
76553
+ liveShiftName,
76273
76554
  lineOptions,
76274
76555
  supervisorOptions,
76275
76556
  selectedSupervisorId,
@@ -76283,6 +76564,14 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76283
76564
  }) => {
76284
76565
  bumpRenderCounter();
76285
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
+ );
76286
76575
  const [isFilterOpen, setIsFilterOpen] = React141__namespace.default.useState(false);
76287
76576
  const [isLinesDropdownOpen, setIsLinesDropdownOpen] = React141__namespace.default.useState(false);
76288
76577
  const filterRef = React141__namespace.default.useRef(null);
@@ -76399,9 +76688,25 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76399
76688
  className: "flex-shrink-0 -ml-1"
76400
76689
  }
76401
76690
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 flex-shrink-0" }),
76402
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center", children: [
76403
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900 text-center px-1 truncate max-w-[200px]", children: "Operations Overview" }),
76404
- /* @__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
+ ] })
76405
76710
  ] }),
76406
76711
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 flex items-center gap-1.5", children: [
76407
76712
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -76420,22 +76725,40 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76420
76725
  {
76421
76726
  ref: mobileFilterButtonRef,
76422
76727
  onClick: handleFilterToggle,
76423
- 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"}`,
76424
76729
  "aria-label": "Open filters",
76425
76730
  children: [
76426
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-5 h-5 ${activeFilterCount > 0 ? "text-blue-600" : "text-gray-700"}` }),
76427
- 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
76428
76733
  ]
76429
76734
  }
76430
76735
  )
76431
76736
  ] })
76432
76737
  ] }) }),
76433
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center relative min-h-[56px]", children: [
76434
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-1/2 -translate-x-1/2 flex flex-col items-center pointer-events-none", children: [
76435
- /* @__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" }),
76436
- /* @__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
+ ] })
76437
76760
  ] }),
76438
- /* @__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: [
76439
76762
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(
76440
76763
  MonthlyRangeFilter_default,
76441
76764
  {
@@ -76452,12 +76775,12 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76452
76775
  {
76453
76776
  ref: filterButtonRef,
76454
76777
  onClick: handleFilterToggle,
76455
- 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"}`,
76456
76779
  "aria-label": "Open filters",
76457
76780
  children: [
76458
- /* @__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"}` }),
76459
76782
  "Filters",
76460
- /* @__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" : ""}` })
76461
76784
  ]
76462
76785
  }
76463
76786
  )
@@ -76585,11 +76908,18 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76585
76908
  const snapshot = useOperationsOverviewSnapshot(store);
76586
76909
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
76587
76910
  const comparisonLabel = React141__namespace.default.useMemo(() => {
76588
- return formatComparisonWindow(
76589
- scope.previous_range?.day_count ?? null,
76590
- scope.comparison_strategy
76591
- );
76592
- }, [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
+ ]);
76593
76923
  const [isIdleContributorsOpen, setIsIdleContributorsOpen] = React141__namespace.default.useState(false);
76594
76924
  const [isIdleContributorsPinned, setIsIdleContributorsPinned] = React141__namespace.default.useState(false);
76595
76925
  const idleContributorsRef = React141__namespace.default.useRef(null);
@@ -76679,10 +77009,17 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76679
77009
  roundOne(snapshot.data.summary.plant_efficiency.current),
76680
77010
  "%"
76681
77011
  ] }),
76682
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 px-2.5 py-1 rounded-full ${plantEfficiencyBadge.className}`, children: [
76683
- 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,
76684
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium", children: plantEfficiencyBadge.text })
76685
- ] })
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
+ )
76686
77023
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-sm text-slate-400", children: "No efficiency data available" })
76687
77024
  ] }),
76688
77025
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -76728,10 +77065,17 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76728
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" }) }),
76729
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: [
76730
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) }),
76731
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1 px-2.5 py-1 rounded-full ${idleBadge.className}`, children: [
76732
- 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,
76733
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium", children: idleBadge.text })
76734
- ] })
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
+ )
76735
77079
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-sm text-slate-400", children: "No idle time data available" })
76736
77080
  ]
76737
77081
  }
@@ -76794,11 +77138,18 @@ var PoorestPerformersCard = React141__namespace.default.memo(({
76794
77138
  }
76795
77139
  }, [availableLineModes?.has_output, availableLineModes?.has_uptime, poorestLineMode]);
76796
77140
  const comparisonLabel = React141__namespace.default.useMemo(() => {
76797
- return formatComparisonWindow(
76798
- scope.previous_range?.day_count ?? null,
76799
- scope.comparison_strategy
76800
- );
76801
- }, [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
+ ]);
76802
77153
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
76803
77154
  const mergedPoorestLines = React141__namespace.default.useMemo(() => {
76804
77155
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
@@ -76955,7 +77306,8 @@ IdleBreakdownCard.displayName = "IdleBreakdownCard";
76955
77306
  var EfficiencyTrendCard = React141__namespace.default.memo(({
76956
77307
  store,
76957
77308
  dateRange,
76958
- appTimezone
77309
+ appTimezone,
77310
+ hourlyLabelStartTime
76959
77311
  }) => {
76960
77312
  bumpRenderCounter();
76961
77313
  const trend = useOperationsOverviewTrend(store);
@@ -76965,7 +77317,41 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
76965
77317
  );
76966
77318
  const isCurrentWeekToDateRange = dateRange.startKey === currentWeekRange.startKey && dateRange.endKey === currentWeekRange.endKey;
76967
77319
  const showInitialSkeleton = trend.loading && trend.lastUpdated === null;
77320
+ const isHourlyTrend = trend.data.granularity === "hour";
76968
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
+ }
76969
77355
  const pointsByDate = new Map(
76970
77356
  (trend.data.points || []).flatMap((point) => {
76971
77357
  if (!point.date) return [];
@@ -77003,12 +77389,13 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
77003
77389
  })()
77004
77390
  };
77005
77391
  });
77006
- }, [currentWeekRange.startKey, isCurrentWeekToDateRange, trend.data.points]);
77392
+ }, [currentWeekRange.startKey, hourlyLabelStartTime, isCurrentWeekToDateRange, isHourlyTrend, trend.data.points]);
77007
77393
  const trendTooltipLabelFormatter = React141__namespace.default.useCallback((label, payload) => {
77394
+ if (isHourlyTrend) return label;
77008
77395
  const dayOfWeek = payload?.[0]?.payload?.dayOfWeek;
77009
77396
  if (!dayOfWeek || typeof label !== "string") return label;
77010
77397
  return `${label} (${dayOfWeek})`;
77011
- }, []);
77398
+ }, [isHourlyTrend]);
77012
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: [
77013
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" }) }),
77014
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(
@@ -77162,7 +77549,8 @@ var useOperationsOverviewRefresh = ({
77162
77549
  endKey,
77163
77550
  trendMode,
77164
77551
  comparisonStrategy,
77165
- isLiveScope
77552
+ isLiveScope,
77553
+ enabled = true
77166
77554
  }) => {
77167
77555
  const lineIdsKey = React141__namespace.default.useMemo(() => lineIds.join(","), [lineIds]);
77168
77556
  const scopeSignature = React141__namespace.default.useMemo(
@@ -77208,7 +77596,7 @@ var useOperationsOverviewRefresh = ({
77208
77596
  }, []);
77209
77597
  const runRefresh = React141__namespace.default.useCallback(
77210
77598
  async (section, begin, onSuccess, onError, request, reason) => {
77211
- if (!supabase || !companyId || lineIds.length === 0) return;
77599
+ if (!enabled || !supabase || !companyId || lineIds.length === 0) return;
77212
77600
  const requestId = requestIdsRef.current[section] + 1;
77213
77601
  requestIdsRef.current[section] = requestId;
77214
77602
  controllersRef.current[section]?.abort();
@@ -77228,7 +77616,7 @@ var useOperationsOverviewRefresh = ({
77228
77616
  onError(error instanceof Error ? error.message : `Failed to refresh ${section}`);
77229
77617
  }
77230
77618
  },
77231
- [companyId, lineIds.length, supabase]
77619
+ [companyId, enabled, lineIds.length, supabase]
77232
77620
  );
77233
77621
  const refreshSnapshot = React141__namespace.default.useCallback(
77234
77622
  async (reason) => {
@@ -77398,6 +77786,12 @@ var useOperationsOverviewRefresh = ({
77398
77786
  });
77399
77787
  }, [refreshAll, startPolling, stopPolling]);
77400
77788
  React141__namespace.default.useEffect(() => {
77789
+ if (!enabled) {
77790
+ stopPolling("disabled");
77791
+ abortAll();
77792
+ store.reset();
77793
+ return;
77794
+ }
77401
77795
  if (!supabase || !companyId || lineIds.length === 0) {
77402
77796
  stopPolling("scope_invalid");
77403
77797
  abortAll();
@@ -77405,9 +77799,9 @@ var useOperationsOverviewRefresh = ({
77405
77799
  return;
77406
77800
  }
77407
77801
  void refreshAll("scope_change");
77408
- }, [abortAll, companyId, lineIds.length, refreshAll, scopeSignature, stopPolling, store, supabase]);
77802
+ }, [abortAll, companyId, enabled, lineIds.length, refreshAll, scopeSignature, stopPolling, store, supabase]);
77409
77803
  React141__namespace.default.useEffect(() => {
77410
- if (!isLiveScope || !supabase || !companyId || lineIds.length === 0) {
77804
+ if (!enabled || !isLiveScope || !supabase || !companyId || lineIds.length === 0) {
77411
77805
  isPageActiveRef.current = false;
77412
77806
  stopPolling("live_scope_disabled");
77413
77807
  return;
@@ -77463,11 +77857,63 @@ var useOperationsOverviewRefresh = ({
77463
77857
  window.removeEventListener("pageshow", handlePageShow);
77464
77858
  window.removeEventListener("pagehide", handlePageHide);
77465
77859
  };
77466
- }, [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;
77467
77911
  };
77468
77912
  var PlantHeadView = () => {
77469
77913
  const supabase = useSupabase();
77470
77914
  const entityConfig = useEntityConfig();
77915
+ const factoryViewId = entityConfig.factoryViewId || "factory";
77916
+ const staticShiftConfig = useShiftConfig();
77471
77917
  const appTimezone = useAppTimezone() || "UTC";
77472
77918
  const { navigate } = useNavigation();
77473
77919
  const { accessibleLineIds } = useUserLineAccess();
@@ -77475,11 +77921,21 @@ var PlantHeadView = () => {
77475
77921
  useHideMobileHeader(!!mobileMenuContext);
77476
77922
  const storeRef = React141__namespace.default.useRef(createOperationsOverviewStore());
77477
77923
  const store = storeRef.current;
77478
- const [dateRange, setDateRange] = React141__namespace.default.useState(() => getCurrentWeekToDateRange(appTimezone));
77479
- 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);
77480
77933
  const [trendMode, setTrendMode] = React141__namespace.default.useState("all");
77481
77934
  const [selectedSupervisorId, setSelectedSupervisorId] = React141__namespace.default.useState("all");
77482
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);
77483
77939
  React141__namespace.default.useEffect(() => {
77484
77940
  trackCorePageView("Operations Overview", {
77485
77941
  dashboard_surface: "operations_overview"
@@ -77501,8 +77957,10 @@ var PlantHeadView = () => {
77501
77957
  return dateRange;
77502
77958
  }, [currentWeekDisplayRange, dateRange, isCurrentWeekToDateRange, usesThisWeekComparison]);
77503
77959
  const normalizedLineIds = React141__namespace.default.useMemo(
77504
- () => Array.from(new Set((accessibleLineIds || []).filter(Boolean))).sort(),
77505
- [accessibleLineIds]
77960
+ () => Array.from(new Set(
77961
+ (accessibleLineIds || []).filter(Boolean).filter((lineId) => lineId !== factoryViewId)
77962
+ )).sort(),
77963
+ [accessibleLineIds, factoryViewId]
77506
77964
  );
77507
77965
  const lineIdsKey = React141__namespace.default.useMemo(
77508
77966
  () => normalizedLineIds.join(","),
@@ -77576,14 +78034,81 @@ var PlantHeadView = () => {
77576
78034
  () => selectedLineIds.length > 0 ? selectedLineIds : normalizedLineIds,
77577
78035
  [normalizedLineIds, selectedLineIds]
77578
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
+ );
77579
78070
  const initializedTimezoneRef = React141__namespace.default.useRef(appTimezone);
77580
78071
  React141__namespace.default.useEffect(() => {
77581
78072
  if (initializedTimezoneRef.current === appTimezone) return;
77582
- setDateRange(getCurrentWeekToDateRange(appTimezone));
77583
- 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);
77584
78082
  initializedTimezoneRef.current = appTimezone;
77585
- }, [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]);
77586
78109
  const handleDateRangeChange = React141__namespace.default.useCallback((range, meta) => {
78110
+ hasUserAdjustedScopeRef.current = true;
78111
+ setIsInitialScopeReady(true);
77587
78112
  trackCoreEvent("Operations Overview Date Range Changed", {
77588
78113
  start_date: range.startKey,
77589
78114
  end_date: range.endKey
@@ -77600,6 +78125,8 @@ var PlantHeadView = () => {
77600
78125
  });
77601
78126
  }, []);
77602
78127
  const handleTrendModeChange = React141__namespace.default.useCallback((mode) => {
78128
+ hasUserAdjustedScopeRef.current = true;
78129
+ setIsInitialScopeReady(true);
77603
78130
  setTrendMode(mode);
77604
78131
  }, []);
77605
78132
  const handleSelectedLineIdsChange = React141__namespace.default.useCallback((lineIds) => {
@@ -77626,15 +78153,6 @@ var PlantHeadView = () => {
77626
78153
  params.set("rangeEnd", dateRange.endKey);
77627
78154
  return `/kpis/${lineId}?${params.toString()}`;
77628
78155
  }, [dateRange.endKey, dateRange.startKey]);
77629
- const handleOpenLineMonthlyHistory = React141__namespace.default.useCallback((lineId, lineName) => {
77630
- trackCoreEvent("Operations Overview Line Clicked", {
77631
- line_id: lineId,
77632
- line_name: lineName,
77633
- range_start: dateRange.startKey,
77634
- range_end: dateRange.endKey
77635
- });
77636
- navigate(buildLineMonthlyHistoryUrl(lineId));
77637
- }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, navigate]);
77638
78156
  const handleViewAllPoorestPerformers = React141__namespace.default.useCallback(() => {
77639
78157
  trackCoreEvent("Operations Overview View All Clicked", { section: "poorest_performers" });
77640
78158
  navigate("/kpis?tab=leaderboard");
@@ -77660,16 +78178,74 @@ var PlantHeadView = () => {
77660
78178
  }
77661
78179
  return void 0;
77662
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]);
77663
78238
  useOperationsOverviewRefresh({
77664
78239
  store,
77665
78240
  supabase,
77666
78241
  companyId: entityConfig.companyId,
77667
78242
  lineIds: scopedLineIds,
77668
- startKey: dateRange.startKey,
77669
- endKey: dateRange.endKey,
77670
- trendMode,
78243
+ startKey: effectiveDateRange.startKey,
78244
+ endKey: effectiveDateRange.endKey,
78245
+ trendMode: effectiveTrendMode,
77671
78246
  comparisonStrategy,
77672
- isLiveScope: isCurrentWeekToDateRange
78247
+ isLiveScope,
78248
+ enabled: scopedLineIds.length > 0 && isShiftScopeResolved
77673
78249
  });
77674
78250
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-h-screen bg-slate-50 w-full font-sans", children: [
77675
78251
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -77678,6 +78254,8 @@ var PlantHeadView = () => {
77678
78254
  dateRange,
77679
78255
  displayDateRange: headerDateRange,
77680
78256
  trendMode,
78257
+ isLiveScope,
78258
+ liveShiftName: currentShiftScope?.shiftName || null,
77681
78259
  lineOptions,
77682
78260
  supervisorOptions,
77683
78261
  selectedSupervisorId,
@@ -77700,7 +78278,7 @@ var PlantHeadView = () => {
77700
78278
  store,
77701
78279
  supervisorsByLineId,
77702
78280
  onViewAll: handleViewAllPoorestPerformers,
77703
- onLineClick: handleOpenLineMonthlyHistory
78281
+ onLineClick: handleOpenLineDetails
77704
78282
  }
77705
78283
  ),
77706
78284
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -77717,7 +78295,8 @@ var PlantHeadView = () => {
77717
78295
  {
77718
78296
  store,
77719
78297
  dateRange,
77720
- appTimezone
78298
+ appTimezone,
78299
+ hourlyLabelStartTime
77721
78300
  }
77722
78301
  ),
77723
78302
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -78578,6 +79157,7 @@ exports.isEfficiencyOnTrack = isEfficiencyOnTrack;
78578
79157
  exports.isFactoryScopedRole = isFactoryScopedRole;
78579
79158
  exports.isFullMonthRange = isFullMonthRange;
78580
79159
  exports.isLegacyConfiguration = isLegacyConfiguration;
79160
+ exports.isLoopbackHostname = isLoopbackHostname;
78581
79161
  exports.isPrefetchError = isPrefetchError;
78582
79162
  exports.isRecentFlowVideoGridMetricMode = isRecentFlowVideoGridMetricMode;
78583
79163
  exports.isSafari = isSafari;
@@ -78619,6 +79199,7 @@ exports.resetSubscriptionManager = resetSubscriptionManager;
78619
79199
  exports.s3VideoPreloader = s3VideoPreloader;
78620
79200
  exports.setSentryUserContext = setSentryUserContext;
78621
79201
  exports.setSentryWorkspaceContext = setSentryWorkspaceContext;
79202
+ exports.shouldEnableLocalDevTestLogin = shouldEnableLocalDevTestLogin;
78622
79203
  exports.shuffleArray = shuffleArray;
78623
79204
  exports.simulateApiDelay = simulateApiDelay;
78624
79205
  exports.skuService = skuService;