@optifye/dashboard-core 6.11.15 → 6.11.17

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
@@ -3295,11 +3295,9 @@ var normalizeShiftDefinitions = (timezone, shiftConfig) => {
3295
3295
  }
3296
3296
  return { shifts: legacyShifts, timezone: fallbackTimezone };
3297
3297
  };
3298
- var determineShiftFromDefinitions = (timezone, shifts, now4 = /* @__PURE__ */ new Date()) => {
3298
+ var determineActiveShiftFromDefinitions = (timezone, shifts, now4 = /* @__PURE__ */ new Date()) => {
3299
3299
  const zonedNow = dateFnsTz.toZonedTime(now4, timezone);
3300
3300
  const currentMinutes = zonedNow.getHours() * 60 + zonedNow.getMinutes();
3301
- let chosen;
3302
- let operationalDate = getOperationalDate(timezone, now4, shifts[0].startTime);
3303
3301
  for (const shift of shifts) {
3304
3302
  const start = parseTimeToMinutes(shift.startTime);
3305
3303
  const endRaw = parseTimeToMinutes(shift.endTime);
@@ -3307,32 +3305,47 @@ var determineShiftFromDefinitions = (timezone, shifts, now4 = /* @__PURE__ */ ne
3307
3305
  const wraps = end <= start;
3308
3306
  if (wraps) end += 1440;
3309
3307
  if (start <= currentMinutes && currentMinutes < end) {
3310
- chosen = shift;
3311
- operationalDate = getOperationalDate(timezone, now4, shift.startTime);
3312
- break;
3308
+ return {
3309
+ shiftId: shift.shiftId,
3310
+ shiftName: shift.shiftName,
3311
+ startTime: shift.startTime,
3312
+ endTime: shift.endTime,
3313
+ timezone,
3314
+ date: getOperationalDate(timezone, now4, shift.startTime)
3315
+ };
3313
3316
  }
3314
3317
  if (start <= currentMinutes + 1440 && currentMinutes + 1440 < end) {
3315
- chosen = shift;
3316
- operationalDate = getOperationalDate(timezone, now4, shift.startTime);
3317
- break;
3318
+ return {
3319
+ shiftId: shift.shiftId,
3320
+ shiftName: shift.shiftName,
3321
+ startTime: shift.startTime,
3322
+ endTime: shift.endTime,
3323
+ timezone,
3324
+ date: getOperationalDate(timezone, now4, shift.startTime)
3325
+ };
3318
3326
  }
3319
3327
  }
3320
- if (!chosen) {
3321
- chosen = shifts[0];
3322
- operationalDate = getOperationalDate(timezone, now4, chosen.startTime);
3328
+ return null;
3329
+ };
3330
+ var getCurrentShift = (timezone, shiftConfig, now4 = /* @__PURE__ */ new Date()) => {
3331
+ const { shifts, timezone: effectiveTz } = normalizeShiftDefinitions(timezone, shiftConfig);
3332
+ const activeShift = determineActiveShiftFromDefinitions(effectiveTz, shifts, now4);
3333
+ if (activeShift) {
3334
+ return activeShift;
3323
3335
  }
3336
+ const fallbackShift = shifts[0];
3324
3337
  return {
3325
- shiftId: chosen.shiftId,
3326
- shiftName: chosen.shiftName,
3327
- startTime: chosen.startTime,
3328
- endTime: chosen.endTime,
3329
- timezone,
3330
- date: operationalDate
3338
+ shiftId: fallbackShift.shiftId,
3339
+ shiftName: fallbackShift.shiftName,
3340
+ startTime: fallbackShift.startTime,
3341
+ endTime: fallbackShift.endTime,
3342
+ timezone: effectiveTz,
3343
+ date: getOperationalDate(effectiveTz, now4, fallbackShift.startTime)
3331
3344
  };
3332
3345
  };
3333
- var getCurrentShift = (timezone, shiftConfig, now4 = /* @__PURE__ */ new Date()) => {
3346
+ var getActiveShift = (timezone, shiftConfig, now4 = /* @__PURE__ */ new Date()) => {
3334
3347
  const { shifts, timezone: effectiveTz } = normalizeShiftDefinitions(timezone, shiftConfig);
3335
- return determineShiftFromDefinitions(effectiveTz, shifts, now4);
3348
+ return determineActiveShiftFromDefinitions(effectiveTz, shifts, now4);
3336
3349
  };
3337
3350
  var isTransitionPeriod = (timezone, shiftConfig, now4 = /* @__PURE__ */ new Date()) => {
3338
3351
  const transitionMinutes = shiftConfig?.transitionPeriodMinutes ?? DEFAULT_TRANSITION_MINUTES;
@@ -12864,6 +12877,7 @@ var useShiftGroups = ({
12864
12877
  }) => {
12865
12878
  const [shiftGroups, setShiftGroups] = React141.useState([]);
12866
12879
  const [shiftGroupsKey, setShiftGroupsKey] = React141.useState("");
12880
+ const [hasComputed, setHasComputed] = React141.useState(false);
12867
12881
  const lastKeyRef = React141.useRef("");
12868
12882
  const mapRef = React141.useRef(shiftConfigMap);
12869
12883
  React141.useEffect(() => {
@@ -12874,6 +12888,7 @@ var useShiftGroups = ({
12874
12888
  lastKeyRef.current = "";
12875
12889
  setShiftGroups([]);
12876
12890
  setShiftGroupsKey("");
12891
+ setHasComputed(false);
12877
12892
  return;
12878
12893
  }
12879
12894
  let isMounted = true;
@@ -12886,6 +12901,7 @@ var useShiftGroups = ({
12886
12901
  setShiftGroups(groups);
12887
12902
  setShiftGroupsKey(key);
12888
12903
  }
12904
+ setHasComputed(true);
12889
12905
  };
12890
12906
  compute();
12891
12907
  const intervalId = setInterval(compute, pollIntervalMs);
@@ -12894,7 +12910,7 @@ var useShiftGroups = ({
12894
12910
  clearInterval(intervalId);
12895
12911
  };
12896
12912
  }, [enabled, shiftConfigMap.size, timezone, pollIntervalMs]);
12897
- return { shiftGroups, shiftGroupsKey };
12913
+ return { shiftGroups, shiftGroupsKey, hasComputed };
12898
12914
  };
12899
12915
 
12900
12916
  // src/lib/types/efficiencyLegend.ts
@@ -12956,52 +12972,6 @@ function getEfficiencyTextColorClasses(efficiency, legend = DEFAULT_EFFICIENCY_L
12956
12972
  }
12957
12973
  }
12958
12974
 
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
12975
  // src/lib/hooks/useDashboardMetrics.ts
13006
12976
  var DEBUG_DASHBOARD_LOGS = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
13007
12977
  var logDebug = (...args) => {
@@ -13090,7 +13060,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13090
13060
  const abortControllerRef = React141.useRef(null);
13091
13061
  const lastFetchKeyRef = React141.useRef(null);
13092
13062
  const inFlightFetchKeyRef = React141.useRef(null);
13093
- const recentFlowCacheRef = React141.useRef(/* @__PURE__ */ new Map());
13094
13063
  const updateQueueRef = React141.useRef(false);
13095
13064
  const onLineMetricsUpdateRef = React141.useRef(onLineMetricsUpdate);
13096
13065
  const shiftGroupsRef = React141.useRef(shiftGroups);
@@ -13124,7 +13093,6 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13124
13093
  setError(null);
13125
13094
  lastFetchKeyRef.current = null;
13126
13095
  inFlightFetchKeyRef.current = null;
13127
- recentFlowCacheRef.current = /* @__PURE__ */ new Map();
13128
13096
  }, [lineId]);
13129
13097
  const fetchAllMetrics = React141.useCallback(async (options = {}) => {
13130
13098
  const { force = false } = options;
@@ -13399,12 +13367,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13399
13367
  actionType: item.action_type,
13400
13368
  actionName: item.action_name
13401
13369
  }),
13402
- recent_flow_mode: item.recent_flow_mode ?? void 0,
13403
13370
  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
13371
  recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
13372
+ recent_flow_computed_at: item.recent_flow_computed_at ?? null,
13408
13373
  incoming_wip_current: item.incoming_wip_current ?? null,
13409
13374
  incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
13410
13375
  incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null
@@ -13415,16 +13380,11 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13415
13380
  const wsNumB = parseInt(b.workspace_name?.replace(/[^0-9]/g, "") || "0");
13416
13381
  return wsNumA - wsNumB;
13417
13382
  });
13418
- const {
13419
- workspaces: mergedWorkspaceData,
13420
- cache: nextRecentFlowCache
13421
- } = mergeWorkspaceRecentFlowMetrics(transformedWorkspaceData, recentFlowCacheRef.current);
13422
- recentFlowCacheRef.current = nextRecentFlowCache;
13423
- mergedWorkspaceData.forEach((metric) => {
13383
+ transformedWorkspaceData.forEach((metric) => {
13424
13384
  workspaceMetricsStore.setOverview(metric);
13425
13385
  });
13426
13386
  const newMetricsState = {
13427
- workspaceMetrics: mergedWorkspaceData,
13387
+ workspaceMetrics: transformedWorkspaceData,
13428
13388
  lineMetrics: allLineMetrics || [],
13429
13389
  metadata: { hasFlowBuffers, idleTimeVlmByLine },
13430
13390
  efficiencyLegend: efficiencyLegend ?? DEFAULT_EFFICIENCY_LEGEND
@@ -16849,7 +16809,7 @@ var useActiveBreaks = (lineIds) => {
16849
16809
  const [isLoading, setIsLoading] = React141.useState(true);
16850
16810
  const [error, setError] = React141.useState(null);
16851
16811
  const supabase = useSupabase();
16852
- const parseTimeToMinutes3 = (timeStr) => {
16812
+ const parseTimeToMinutes4 = (timeStr) => {
16853
16813
  const [hours, minutes] = timeStr.split(":").map(Number);
16854
16814
  return hours * 60 + minutes;
16855
16815
  };
@@ -16858,8 +16818,8 @@ var useActiveBreaks = (lineIds) => {
16858
16818
  return now4.getHours() * 60 + now4.getMinutes();
16859
16819
  };
16860
16820
  const isTimeInBreak = (breakStart, breakEnd, currentMinutes) => {
16861
- const startMinutes = parseTimeToMinutes3(breakStart);
16862
- const endMinutes = parseTimeToMinutes3(breakEnd);
16821
+ const startMinutes = parseTimeToMinutes4(breakStart);
16822
+ const endMinutes = parseTimeToMinutes4(breakEnd);
16863
16823
  if (endMinutes < startMinutes) {
16864
16824
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
16865
16825
  } else {
@@ -16867,8 +16827,8 @@ var useActiveBreaks = (lineIds) => {
16867
16827
  }
16868
16828
  };
16869
16829
  const calculateBreakProgress = (breakStart, breakEnd, currentMinutes) => {
16870
- const startMinutes = parseTimeToMinutes3(breakStart);
16871
- const endMinutes = parseTimeToMinutes3(breakEnd);
16830
+ const startMinutes = parseTimeToMinutes4(breakStart);
16831
+ const endMinutes = parseTimeToMinutes4(breakEnd);
16872
16832
  let elapsedMinutes = 0;
16873
16833
  let remainingMinutes = 0;
16874
16834
  if (endMinutes < startMinutes) {
@@ -16886,8 +16846,8 @@ var useActiveBreaks = (lineIds) => {
16886
16846
  return { elapsedMinutes, remainingMinutes };
16887
16847
  };
16888
16848
  const isTimeInShift = (startTime, endTime, currentMinutes) => {
16889
- const startMinutes = parseTimeToMinutes3(startTime);
16890
- const endMinutes = parseTimeToMinutes3(endTime);
16849
+ const startMinutes = parseTimeToMinutes4(startTime);
16850
+ const endMinutes = parseTimeToMinutes4(endTime);
16891
16851
  if (endMinutes < startMinutes) {
16892
16852
  return currentMinutes >= startMinutes || currentMinutes < endMinutes;
16893
16853
  } else {
@@ -16947,8 +16907,8 @@ var useActiveBreaks = (lineIds) => {
16947
16907
  const endTime = breakItem.end || breakItem.endTime || "00:00";
16948
16908
  let duration = breakItem.duration || 0;
16949
16909
  if (!duration || duration === 0) {
16950
- const startMinutes = parseTimeToMinutes3(startTime);
16951
- const endMinutes = parseTimeToMinutes3(endTime);
16910
+ const startMinutes = parseTimeToMinutes4(startTime);
16911
+ const endMinutes = parseTimeToMinutes4(endTime);
16952
16912
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
16953
16913
  }
16954
16914
  return {
@@ -16964,8 +16924,8 @@ var useActiveBreaks = (lineIds) => {
16964
16924
  const endTime = breakItem.end || breakItem.endTime || "00:00";
16965
16925
  let duration = breakItem.duration || 0;
16966
16926
  if (!duration || duration === 0) {
16967
- const startMinutes = parseTimeToMinutes3(startTime);
16968
- const endMinutes = parseTimeToMinutes3(endTime);
16927
+ const startMinutes = parseTimeToMinutes4(startTime);
16928
+ const endMinutes = parseTimeToMinutes4(endTime);
16969
16929
  duration = endMinutes < startMinutes ? endMinutes + 24 * 60 - startMinutes : endMinutes - startMinutes;
16970
16930
  }
16971
16931
  return {
@@ -23046,6 +23006,16 @@ var createThrottledReload = (interval = 5e3, maxReloads = 3) => {
23046
23006
  };
23047
23007
  var throttledReloadDashboard = createThrottledReload(5e3, 3);
23048
23008
 
23009
+ // src/lib/utils/dev/localDevTestLogin.ts
23010
+ var isLoopbackHostname = (hostname) => {
23011
+ const normalized = String(hostname || "").trim().toLowerCase();
23012
+ return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1";
23013
+ };
23014
+ var shouldEnableLocalDevTestLogin = ({
23015
+ enabledFlag,
23016
+ hostname
23017
+ }) => enabledFlag && isLoopbackHostname(hostname);
23018
+
23049
23019
  // src/lib/utils/index.ts
23050
23020
  var formatIdleTime = (idleTimeInSeconds) => {
23051
23021
  if (!idleTimeInSeconds || idleTimeInSeconds <= 0) {
@@ -31230,12 +31200,16 @@ var LoginPage = ({
31230
31200
  onRateLimitCheck,
31231
31201
  logoSrc = optifye_logo_default,
31232
31202
  logoAlt = "Optifye",
31233
- brandName = "Optifye"
31203
+ brandName = "Optifye",
31204
+ showDevTestLogin = false,
31205
+ devTestLoginLabel = "Sign in as Test User",
31206
+ onDevTestLogin
31234
31207
  }) => {
31235
31208
  const [email, setEmail] = React141.useState("");
31236
31209
  const [otp, setOtp] = React141.useState("");
31237
31210
  const [step, setStep] = React141.useState("email");
31238
31211
  const [loading, setLoading] = React141.useState(false);
31212
+ const [devLoginLoading, setDevLoginLoading] = React141.useState(false);
31239
31213
  const [error, setError] = React141.useState(null);
31240
31214
  const [countdown, setCountdown] = React141.useState(0);
31241
31215
  const supabase = useSupabase();
@@ -31299,6 +31273,25 @@ var LoginPage = ({
31299
31273
  startCountdown();
31300
31274
  }
31301
31275
  };
31276
+ const handleDevTestLogin = async () => {
31277
+ if (!onDevTestLogin) {
31278
+ return;
31279
+ }
31280
+ setDevLoginLoading(true);
31281
+ setError(null);
31282
+ try {
31283
+ const result = await onDevTestLogin();
31284
+ const nextError = typeof result === "object" && result !== null && "error" in result ? result.error : null;
31285
+ if (typeof nextError === "string" && nextError.trim()) {
31286
+ setError(nextError);
31287
+ }
31288
+ } catch (err) {
31289
+ console.error("Dev test login failed:", err);
31290
+ setError("Dev test login unavailable.");
31291
+ } finally {
31292
+ setDevLoginLoading(false);
31293
+ }
31294
+ };
31302
31295
  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
31296
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-2xl shadow-xl border border-slate-200 p-8", children: [
31304
31297
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8", children: [
@@ -31339,7 +31332,7 @@ var LoginPage = ({
31339
31332
  "button",
31340
31333
  {
31341
31334
  type: "submit",
31342
- disabled: loading,
31335
+ disabled: loading || devLoginLoading,
31343
31336
  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
31337
  children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31345
31338
  /* @__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 +31342,31 @@ var LoginPage = ({
31349
31342
  "Sending..."
31350
31343
  ] }) : "Continue with Email"
31351
31344
  }
31352
- )
31345
+ ),
31346
+ showDevTestLogin && onDevTestLogin ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
31347
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
31348
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px flex-1 bg-slate-200" }),
31349
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium uppercase tracking-[0.2em] text-slate-400", children: "Local Dev" }),
31350
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px flex-1 bg-slate-200" })
31351
+ ] }),
31352
+ /* @__PURE__ */ jsxRuntime.jsx(
31353
+ "button",
31354
+ {
31355
+ type: "button",
31356
+ disabled: loading || devLoginLoading,
31357
+ onClick: () => void handleDevTestLogin(),
31358
+ 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",
31359
+ children: devLoginLoading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31360
+ /* @__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: [
31361
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
31362
+ /* @__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" })
31363
+ ] }),
31364
+ "Signing In..."
31365
+ ] }) : devTestLoginLabel
31366
+ }
31367
+ ),
31368
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-xs text-slate-500", children: "Localhost-only convenience login for the shared test account." })
31369
+ ] }) : null
31353
31370
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "space-y-6", onSubmit: handleVerifyOTP, children: [
31354
31371
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
31355
31372
  /* @__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 +31391,7 @@ var LoginPage = ({
31374
31391
  "button",
31375
31392
  {
31376
31393
  type: "submit",
31377
- disabled: loading || otp.length !== 6,
31394
+ disabled: loading || devLoginLoading || otp.length !== 6,
31378
31395
  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
31396
  children: loading ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31380
31397
  /* @__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: [
@@ -32199,6 +32216,7 @@ var LineChartComponent = ({
32199
32216
  xAxisLabel,
32200
32217
  xAxisTickFormatter,
32201
32218
  // Pass through for X-axis tick formatting
32219
+ xAxisInterval,
32202
32220
  yAxisLabel,
32203
32221
  yAxisUnit,
32204
32222
  yAxisDomain,
@@ -32215,31 +32233,35 @@ var LineChartComponent = ({
32215
32233
  ...restOfChartProps
32216
32234
  }) => {
32217
32235
  const containerRef = React141__namespace.default.useRef(null);
32218
- const [containerReady, setContainerReady] = React141__namespace.default.useState(false);
32219
- const themeConfig = useThemeConfig();
32220
- const { formatNumber } = useFormatNumber();
32236
+ const [dimensions, setDimensions] = React141__namespace.default.useState({ width: 0, height: 0 });
32237
+ const [hasValidData, setHasValidData] = React141__namespace.default.useState(false);
32221
32238
  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);
32239
+ const currentHasValidData = data && lines && lines.length > 0 && data.some(
32240
+ (item) => lines.some((line) => {
32241
+ const val = item[line.dataKey];
32242
+ return typeof val === "number" && val > 0;
32243
+ })
32244
+ );
32245
+ if (currentHasValidData && !hasValidData) {
32246
+ setHasValidData(true);
32234
32247
  }
32235
- const fallbackTimeout = setTimeout(() => {
32236
- setContainerReady(true);
32237
- }, 100);
32238
- return () => {
32239
- resizeObserver.disconnect();
32240
- clearTimeout(fallbackTimeout);
32241
- };
32248
+ }, [data, lines, hasValidData]);
32249
+ React141__namespace.default.useEffect(() => {
32250
+ if (!containerRef.current) return;
32251
+ const observer = new ResizeObserver((entries) => {
32252
+ const entry = entries[0];
32253
+ if (entry) {
32254
+ setDimensions({
32255
+ width: entry.contentRect.width,
32256
+ height: entry.contentRect.height
32257
+ });
32258
+ }
32259
+ });
32260
+ observer.observe(containerRef.current);
32261
+ return () => observer.disconnect();
32242
32262
  }, []);
32263
+ const themeConfig = useThemeConfig();
32264
+ const { formatNumber } = useFormatNumber();
32243
32265
  const yAxisTickFormatter = (value) => {
32244
32266
  return `${formatNumber(value)}${yAxisUnit || ""}`;
32245
32267
  };
@@ -32261,57 +32283,72 @@ var LineChartComponent = ({
32261
32283
  const gridStrokeColor = themeConfig?.gray?.["300"] || "#ccc";
32262
32284
  const axisTickFillColor = themeConfig?.gray?.["600"] || "#666";
32263
32285
  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
- ] });
32286
+ const renderChartContent = (chartWidth, chartHeight) => /* @__PURE__ */ jsxRuntime.jsxs(
32287
+ recharts.LineChart,
32288
+ {
32289
+ width: chartWidth,
32290
+ height: chartHeight,
32291
+ data,
32292
+ margin: { top: 5, right: 30, left: 20, bottom: 20 },
32293
+ ...restOfChartProps,
32294
+ children: [
32295
+ showGrid && /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: gridStrokeColor }),
32296
+ /* @__PURE__ */ jsxRuntime.jsx(
32297
+ recharts.XAxis,
32298
+ {
32299
+ dataKey: xAxisDataKey,
32300
+ label: xAxisLabel ? { value: xAxisLabel, position: "insideBottom", offset: -10 } : void 0,
32301
+ tickFormatter: xAxisTickFormatter,
32302
+ interval: xAxisInterval,
32303
+ tick: { fontSize: 12, fill: axisTickFillColor },
32304
+ stroke: axisStrokeColor
32305
+ }
32306
+ ),
32307
+ /* @__PURE__ */ jsxRuntime.jsx(
32308
+ recharts.YAxis,
32309
+ {
32310
+ label: yAxisLabel ? { value: yAxisLabel, angle: -90, position: "insideLeft" } : void 0,
32311
+ tickFormatter: yAxisTickFormatter,
32312
+ domain: yAxisDomain,
32313
+ tick: { fontSize: 12, fill: axisTickFillColor },
32314
+ stroke: axisStrokeColor
32315
+ }
32316
+ ),
32317
+ showTooltip && /* @__PURE__ */ jsxRuntime.jsx(
32318
+ recharts.Tooltip,
32319
+ {
32320
+ formatter: tooltipFormatter || defaultTooltipFormatter,
32321
+ labelFormatter: tooltipLabelFormatter,
32322
+ itemStyle: { color: "#111827" },
32323
+ cursor: { strokeDasharray: "3 3" }
32324
+ }
32325
+ ),
32326
+ showLegend && /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { payload: legendPayload }),
32327
+ lines.map((lineConfig, index) => {
32328
+ const lineProps = {
32329
+ ...lineConfig,
32330
+ key: lineConfig.dataKey,
32331
+ type: lineConfig.type || "monotone",
32332
+ stroke: lineConfig.stroke || defaultColors[index % defaultColors.length],
32333
+ activeDot: lineConfig.activeDot !== void 0 ? lineConfig.activeDot : { r: 6 },
32334
+ isAnimationActive: true,
32335
+ animationDuration: 1500,
32336
+ animationBegin: 300
32337
+ };
32338
+ return /* @__PURE__ */ jsxRuntime.jsx(recharts.Line, { ...lineProps, children: lineConfig.labelList && /* @__PURE__ */ jsxRuntime.jsx(
32339
+ recharts.LabelList,
32340
+ {
32341
+ dataKey: lineConfig.dataKey,
32342
+ position: "top",
32343
+ formatter: (value) => formatNumber(value),
32344
+ ...typeof lineConfig.labelList === "object" ? lineConfig.labelList : {}
32345
+ }
32346
+ ) });
32347
+ })
32348
+ ]
32349
+ },
32350
+ hasValidData ? "valid" : "empty"
32351
+ );
32315
32352
  if (responsive) {
32316
32353
  return /* @__PURE__ */ jsxRuntime.jsx(
32317
32354
  "div",
@@ -32319,11 +32356,20 @@ var LineChartComponent = ({
32319
32356
  ref: containerRef,
32320
32357
  className: clsx(fillContainer ? "w-full h-full" : "w-full h-auto", className),
32321
32358
  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..." }) })
32359
+ children: /* @__PURE__ */ jsxRuntime.jsx(
32360
+ motion.div,
32361
+ {
32362
+ initial: { opacity: 0 },
32363
+ animate: { opacity: 1 },
32364
+ transition: { duration: 0.5 },
32365
+ className: "w-full h-full",
32366
+ children: dimensions.width > 0 && dimensions.height > 0 && renderChartContent(dimensions.width, dimensions.height)
32367
+ }
32368
+ )
32323
32369
  }
32324
32370
  );
32325
32371
  }
32326
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("w-full", className), children: chartContent });
32372
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("w-full", className), children: renderChartContent(restOfChartProps.width, restOfChartProps.height) });
32327
32373
  };
32328
32374
  var LineChart = React141__namespace.default.memo(LineChartComponent, (prevProps, nextProps) => {
32329
32375
  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 +32637,35 @@ var CycleTimeOverTimeChart = ({
32591
32637
  }) => {
32592
32638
  const MAX_DATA_POINTS = 40;
32593
32639
  const containerRef = React141__namespace.default.useRef(null);
32594
- const [containerReady, setContainerReady] = React141__namespace.default.useState(false);
32595
- const parseTimeToMinutes3 = (value) => {
32640
+ const [dimensions, setDimensions] = React141__namespace.default.useState({ width: 0, height: 0 });
32641
+ const [hasValidData, setHasValidData] = React141__namespace.default.useState(false);
32642
+ React141__namespace.default.useEffect(() => {
32643
+ const currentHasValidData = data && data.some((val) => val !== null && val > 0);
32644
+ if (currentHasValidData && !hasValidData) {
32645
+ setHasValidData(true);
32646
+ }
32647
+ }, [data, hasValidData]);
32648
+ React141__namespace.default.useEffect(() => {
32649
+ if (!containerRef.current) return;
32650
+ const observer = new ResizeObserver((entries) => {
32651
+ const entry = entries[0];
32652
+ if (entry) {
32653
+ setDimensions({
32654
+ width: entry.contentRect.width,
32655
+ height: entry.contentRect.height
32656
+ });
32657
+ }
32658
+ });
32659
+ observer.observe(containerRef.current);
32660
+ return () => observer.disconnect();
32661
+ }, []);
32662
+ const parseTimeToMinutes4 = (value) => {
32596
32663
  const [hours, minutes] = value.split(":").map(Number);
32597
32664
  if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return 0;
32598
32665
  return hours * 60 + minutes;
32599
32666
  };
32600
32667
  const formatHourLabel = (slotIndex) => {
32601
- const baseMinutes = parseTimeToMinutes3(shiftStart);
32668
+ const baseMinutes = parseTimeToMinutes4(shiftStart);
32602
32669
  const absoluteMinutes = baseMinutes + slotIndex * 60;
32603
32670
  const hour24 = Math.floor(absoluteMinutes % (24 * 60) / 60);
32604
32671
  const ampm = hour24 >= 12 ? "PM" : "AM";
@@ -32617,52 +32684,7 @@ var CycleTimeOverTimeChart = ({
32617
32684
  const displayData = getDisplayData(data);
32618
32685
  const DURATION = displayData.length;
32619
32686
  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
32687
  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
32688
  const labelInterval = React141__namespace.default.useMemo(() => {
32667
32689
  if (xAxisMode === "hourly") {
32668
32690
  return Math.max(1, Math.ceil(DURATION / 8));
@@ -32870,144 +32892,154 @@ var CycleTimeOverTimeChart = ({
32870
32892
  return /* @__PURE__ */ jsxRuntime.jsxs(
32871
32893
  "div",
32872
32894
  {
32873
- ref: containerRef,
32874
32895
  className: `w-full h-full min-w-0 flex flex-col relative pb-2 ${className}`,
32875
32896
  style: { minHeight: "200px", minWidth: 0 },
32876
32897
  children: [
32877
32898
  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,
32899
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 w-full", ref: containerRef, children: /* @__PURE__ */ jsxRuntime.jsx(
32900
+ motion.div,
32880
32901
  {
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"
32902
+ initial: { opacity: 0 },
32903
+ animate: { opacity: 1 },
32904
+ transition: { duration: 0.5 },
32905
+ className: "w-full h-full",
32906
+ children: dimensions.width > 0 && dimensions.height > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
32907
+ recharts.LineChart,
32908
+ {
32909
+ width: dimensions.width,
32910
+ height: dimensions.height,
32911
+ data: chartData,
32912
+ margin: {
32913
+ top: 5,
32914
+ right: 30,
32915
+ bottom: 25,
32916
+ left: 10
33005
32917
  },
33006
- `${effectiveDatasetKey}:idle`
33007
- )
33008
- ]
32918
+ children: [
32919
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
32920
+ /* @__PURE__ */ jsxRuntime.jsx(
32921
+ recharts.XAxis,
32922
+ {
32923
+ dataKey: "label",
32924
+ tick: { fontSize: 11 },
32925
+ interval: 0,
32926
+ angle: xAxisMode === "hourly" ? 0 : -30,
32927
+ textAnchor: xAxisMode === "hourly" ? "middle" : "end",
32928
+ tickMargin: xAxisMode === "hourly" ? 8 : 15,
32929
+ height: xAxisMode === "hourly" ? 40 : 60
32930
+ }
32931
+ ),
32932
+ /* @__PURE__ */ jsxRuntime.jsx(
32933
+ recharts.YAxis,
32934
+ {
32935
+ tickMargin: 8,
32936
+ width: 45,
32937
+ yAxisId: "cycle",
32938
+ domain: ["auto", "auto"],
32939
+ ticks: [0, idealCycleTime, ...Array.from({ length: 4 }, (_, i) => (i + 1) * Math.ceil(idealCycleTime / 2))].sort((a, b) => a - b),
32940
+ tickFormatter: (value) => String(value),
32941
+ tick: (props) => {
32942
+ const { x, y, payload } = props;
32943
+ const displayValue = typeof payload.value === "number" ? payload.value.toFixed(1) : String(payload.value);
32944
+ return /* @__PURE__ */ jsxRuntime.jsx("g", { transform: `translate(${x},${y})`, children: /* @__PURE__ */ jsxRuntime.jsx(
32945
+ "text",
32946
+ {
32947
+ x: 0,
32948
+ y: 0,
32949
+ dy: 4,
32950
+ textAnchor: "end",
32951
+ fill: payload.value === idealCycleTime ? "#E34329" : "#666",
32952
+ fontSize: 12,
32953
+ fontWeight: payload.value === idealCycleTime ? "bold" : "normal",
32954
+ children: displayValue
32955
+ },
32956
+ `tick-${payload.value}-${x}-${y}`
32957
+ ) });
32958
+ }
32959
+ }
32960
+ ),
32961
+ showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
32962
+ recharts.YAxis,
32963
+ {
32964
+ yAxisId: "idle",
32965
+ orientation: "right",
32966
+ tickMargin: 8,
32967
+ width: 35,
32968
+ domain: [0, 60],
32969
+ tickFormatter: (value) => `${value}m`,
32970
+ tick: { fontSize: 11, fill: "#f59e0b" },
32971
+ axisLine: false,
32972
+ tickLine: false
32973
+ }
32974
+ ),
32975
+ /* @__PURE__ */ jsxRuntime.jsx(
32976
+ recharts.Tooltip,
32977
+ {
32978
+ cursor: { stroke: "#E5E7EB", strokeWidth: 1 },
32979
+ content: renderChartTooltip,
32980
+ animationDuration: 200
32981
+ }
32982
+ ),
32983
+ /* @__PURE__ */ jsxRuntime.jsx(
32984
+ recharts.ReferenceLine,
32985
+ {
32986
+ y: idealCycleTime,
32987
+ yAxisId: "cycle",
32988
+ stroke: "#E34329",
32989
+ strokeDasharray: "3 3",
32990
+ strokeWidth: 2,
32991
+ label: {
32992
+ position: "right",
32993
+ value: `${idealCycleTime.toFixed(1)}s`,
32994
+ fill: "#E34329",
32995
+ fontSize: 12,
32996
+ fontWeight: 500
32997
+ }
32998
+ }
32999
+ ),
33000
+ /* @__PURE__ */ jsxRuntime.jsx(
33001
+ recharts.Line,
33002
+ {
33003
+ type: "monotone",
33004
+ yAxisId: "cycle",
33005
+ dataKey: "cycleTime",
33006
+ stroke: "#3B82F6",
33007
+ strokeWidth: 2,
33008
+ connectNulls: false,
33009
+ dot: renderCycleDot,
33010
+ activeDot: renderCycleActiveDot,
33011
+ isAnimationActive: true,
33012
+ animationBegin: 300,
33013
+ animationDuration: 1500,
33014
+ animationEasing: "ease-out"
33015
+ },
33016
+ `${effectiveDatasetKey}:cycle`
33017
+ ),
33018
+ showIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
33019
+ recharts.Line,
33020
+ {
33021
+ type: "monotone",
33022
+ yAxisId: "idle",
33023
+ dataKey: "idleMinutes",
33024
+ stroke: "#f59e0b",
33025
+ strokeWidth: 2,
33026
+ strokeDasharray: "4 4",
33027
+ connectNulls: false,
33028
+ dot: renderIdleDot,
33029
+ activeDot: renderIdleActiveDot,
33030
+ isAnimationActive: true,
33031
+ animationBegin: 300,
33032
+ animationDuration: 1500,
33033
+ animationEasing: "ease-out"
33034
+ },
33035
+ `${effectiveDatasetKey}:idle`
33036
+ )
33037
+ ]
33038
+ },
33039
+ hasValidData ? "valid" : "empty"
33040
+ )
33009
33041
  }
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..." }) }) })
33042
+ ) })
33011
33043
  ]
33012
33044
  }
33013
33045
  );
@@ -33857,10 +33889,10 @@ var HourlyOutputChart = React141__namespace.default.memo(HourlyOutputChartCompon
33857
33889
  HourlyOutputChart.displayName = "HourlyOutputChart";
33858
33890
 
33859
33891
  // src/components/dashboard/grid/videoGridMetricUtils.ts
33860
- var VIDEO_GRID_LEGEND_LABEL = "Flow";
33892
+ var VIDEO_GRID_LEGEND_LABEL = "7 Minute Efficiency";
33861
33893
  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);
33894
+ var MIXED_VIDEO_GRID_LEGEND_LABEL = "Efficiency";
33895
+ var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
33864
33896
  var isVideoGridRecentFlowEnabled = (workspace) => isRecentFlowVideoGridMetricMode(
33865
33897
  workspace.video_grid_metric_mode,
33866
33898
  workspace.assembly_enabled === true
@@ -33869,11 +33901,11 @@ var isVideoGridWipGated = (workspace) => isWipGatedVideoGridMetricMode(
33869
33901
  workspace.video_grid_metric_mode,
33870
33902
  workspace.assembly_enabled === true
33871
33903
  );
33872
- var hasVideoGridRecentFlow = (workspace) => isVideoGridRecentFlowEnabled(workspace) && isFiniteNumber2(workspace.recent_flow_percent);
33904
+ var hasVideoGridRecentFlow = (workspace) => isVideoGridRecentFlowEnabled(workspace) && isFiniteNumber(workspace.recent_flow_percent);
33873
33905
  var isVideoGridRecentFlowUnavailable = (workspace) => isVideoGridRecentFlowEnabled(workspace) && !hasVideoGridRecentFlow(workspace);
33874
33906
  var getVideoGridMetricValue = (workspace) => {
33875
33907
  const recentFlowPercent = workspace.recent_flow_percent;
33876
- if (hasVideoGridRecentFlow(workspace) && isFiniteNumber2(recentFlowPercent)) {
33908
+ if (hasVideoGridRecentFlow(workspace) && isFiniteNumber(recentFlowPercent)) {
33877
33909
  return recentFlowPercent;
33878
33910
  }
33879
33911
  if (isVideoGridRecentFlowUnavailable(workspace)) {
@@ -33884,7 +33916,7 @@ var getVideoGridMetricValue = (workspace) => {
33884
33916
  var hasIncomingWipMapping = (workspace) => Boolean(workspace.incoming_wip_buffer_name);
33885
33917
  var getVideoGridBaseColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33886
33918
  const metricValue = getVideoGridMetricValue(workspace);
33887
- if (!isFiniteNumber2(metricValue)) {
33919
+ if (!isFiniteNumber(metricValue)) {
33888
33920
  return "neutral";
33889
33921
  }
33890
33922
  return getEfficiencyColor(metricValue, legend);
@@ -33899,7 +33931,7 @@ var isLowWipGreenOverride = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) => {
33899
33931
  if (!hasIncomingWipMapping(workspace)) {
33900
33932
  return false;
33901
33933
  }
33902
- return isFiniteNumber2(workspace.incoming_wip_current) && workspace.incoming_wip_current <= 1;
33934
+ return isFiniteNumber(workspace.incoming_wip_current) && workspace.incoming_wip_current <= 1;
33903
33935
  };
33904
33936
  var toMinuteBucket = (minuteBucket) => Number.isFinite(minuteBucket) ? Math.floor(minuteBucket) : Math.floor(Date.now() / 6e4);
33905
33937
  var getEffectiveFlowMinuteBucket = (workspace) => {
@@ -33941,7 +33973,7 @@ var getVideoGridColorState = (workspace, legend = DEFAULT_EFFICIENCY_LEGEND) =>
33941
33973
  if (!hasIncomingWipMapping(workspace)) {
33942
33974
  return baseColor;
33943
33975
  }
33944
- if (!isFiniteNumber2(workspace.incoming_wip_current)) {
33976
+ if (!isFiniteNumber(workspace.incoming_wip_current)) {
33945
33977
  return "neutral";
33946
33978
  }
33947
33979
  if (isLowWipGreenOverride(workspace, legend)) {
@@ -34011,8 +34043,9 @@ var VideoCard = React141__namespace.default.memo(({
34011
34043
  const isRecentFlowCard = isVideoGridRecentFlowEnabled(workspace);
34012
34044
  const hasDisplayMetric = typeof videoGridDisplayValue === "number" && Number.isFinite(videoGridDisplayValue);
34013
34045
  const hasBarMetric = typeof videoGridMetricValue === "number" && Number.isFinite(videoGridMetricValue);
34046
+ const shouldRenderMetricBadge = hasDisplayMetric;
34014
34047
  const badgeTitle = hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
34015
- const badgeLabel = hasDisplayMetric ? `${Math.round(videoGridDisplayValue)}%` : "X";
34048
+ const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;
34016
34049
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
34017
34050
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
34018
34051
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -34088,7 +34121,7 @@ var VideoCard = React141__namespace.default.memo(({
34088
34121
  lastSeenText
34089
34122
  ] })
34090
34123
  ] }) }),
34091
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute ${compact ? "top-1 right-1" : "top-2 right-2"} z-30`, children: /* @__PURE__ */ jsxRuntime.jsx(
34124
+ shouldRenderMetricBadge && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute ${compact ? "top-1 right-1" : "top-2 right-2"} z-30`, children: /* @__PURE__ */ jsxRuntime.jsx(
34092
34125
  "div",
34093
34126
  {
34094
34127
  "data-testid": "video-card-metric-badge",
@@ -34133,7 +34166,7 @@ var VideoCard = React141__namespace.default.memo(({
34133
34166
  }
34134
34167
  );
34135
34168
  }, (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) {
34169
+ 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
34170
  return false;
34138
34171
  }
34139
34172
  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 +44634,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44601
44634
  return null;
44602
44635
  }
44603
44636
  };
44604
- const getShiftIcon = (shift) => {
44637
+ const getShiftIcon2 = (shift) => {
44605
44638
  if (shift === "Day") {
44606
44639
  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
44640
  } else {
@@ -44627,7 +44660,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44627
44660
  }
44628
44661
  if (variant === "enhanced") {
44629
44662
  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) }),
44663
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon2(currentShiftText) }),
44631
44664
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-base font-medium text-blue-800", children: [
44632
44665
  currentShiftText,
44633
44666
  " Shift"
@@ -44635,7 +44668,7 @@ var ShiftDisplay = React141.memo(({ className, variant = "default", lineId }) =>
44635
44668
  ] });
44636
44669
  }
44637
44670
  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) }),
44671
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-800", children: getShiftIcon2(currentShiftText) }),
44639
44672
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-base font-medium text-blue-800", children: [
44640
44673
  currentShiftText,
44641
44674
  " Shift"
@@ -47272,7 +47305,7 @@ var LinePdfGenerator = ({
47272
47305
  doc.setLineWidth(0.8);
47273
47306
  doc.line(20, 123, 190, 123);
47274
47307
  const hourlyOverviewStartY = 128;
47275
- const parseTimeToMinutes3 = (timeStr) => {
47308
+ const parseTimeToMinutes4 = (timeStr) => {
47276
47309
  const [hours, minutes] = timeStr.split(":");
47277
47310
  const hour = parseInt(hours, 10);
47278
47311
  const minute = parseInt(minutes || "0", 10);
@@ -47305,7 +47338,7 @@ var LinePdfGenerator = ({
47305
47338
  };
47306
47339
  };
47307
47340
  const getHourlyTimeRanges = (startTimeStr, endTimeStr) => {
47308
- const startMinutes = parseTimeToMinutes3(startTimeStr);
47341
+ const startMinutes = parseTimeToMinutes4(startTimeStr);
47309
47342
  if (Number.isNaN(startMinutes)) {
47310
47343
  return [];
47311
47344
  }
@@ -47313,7 +47346,7 @@ var LinePdfGenerator = ({
47313
47346
  const defaultHours = 11;
47314
47347
  return Array.from({ length: defaultHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
47315
47348
  }
47316
- const endMinutes = parseTimeToMinutes3(endTimeStr);
47349
+ const endMinutes = parseTimeToMinutes4(endTimeStr);
47317
47350
  if (Number.isNaN(endMinutes)) {
47318
47351
  const fallbackHours = 11;
47319
47352
  return Array.from({ length: fallbackHours }, (_, i) => buildRange(startMinutes + i * 60, 60));
@@ -49082,7 +49115,7 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
49082
49115
  minute: "2-digit",
49083
49116
  hour12: true
49084
49117
  });
49085
- const parseTimeToMinutes3 = (timeValue) => {
49118
+ const parseTimeToMinutes4 = (timeValue) => {
49086
49119
  const [hourPart, minutePart] = timeValue.split(":").map(Number);
49087
49120
  const hour = Number.isFinite(hourPart) ? hourPart : 0;
49088
49121
  const minute = Number.isFinite(minutePart) ? minutePart : 0;
@@ -49099,8 +49132,8 @@ var WorkspacePdfGenerator = ({ workspace, className, idleTimeReasons, efficiency
49099
49132
  const IST_OFFSET_MINUTES = 330;
49100
49133
  return Date.UTC(year, month - 1, day, hour, minute) - IST_OFFSET_MINUTES * 60 * 1e3;
49101
49134
  };
49102
- const shiftStartMinutes = parseTimeToMinutes3(workspace.shift_start);
49103
- const shiftEndMinutes = parseTimeToMinutes3(workspace.shift_end);
49135
+ const shiftStartMinutes = parseTimeToMinutes4(workspace.shift_start);
49136
+ const shiftEndMinutes = parseTimeToMinutes4(workspace.shift_end);
49104
49137
  const wrapsMidnight = shiftEndMinutes <= shiftStartMinutes;
49105
49138
  const shiftStartUtcMs = toShiftUtcMs(workspace.date, workspace.shift_start);
49106
49139
  const shiftEndUtcMs = toShiftUtcMs(workspace.date, workspace.shift_end) + (wrapsMidnight ? 24 * 60 * 60 * 1e3 : 0);
@@ -49815,8 +49848,7 @@ var WorkspaceCycleTimeMetricCards = ({
49815
49848
  {
49816
49849
  data: idleTimeData.chartData,
49817
49850
  isLoading: idleTimeData.isLoading,
49818
- error: idleTimeData.error,
49819
- variant: "bar"
49851
+ error: idleTimeData.error
49820
49852
  }
49821
49853
  ) })
49822
49854
  ] })
@@ -51407,7 +51439,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51407
51439
  const rawName = currentShift.shiftName || "Day";
51408
51440
  return rawName.toLowerCase().includes("shift") ? rawName : `${rawName} Shift`;
51409
51441
  };
51410
- const getShiftIcon = () => {
51442
+ const getShiftIcon2 = () => {
51411
51443
  const currentShift = getCurrentShift(timezone, shiftConfig);
51412
51444
  const shiftName = (currentShift.shiftName || "").toLowerCase();
51413
51445
  if (shiftName.includes("day") || shiftName.includes("morning") || currentShift.shiftId === 0) {
@@ -51441,7 +51473,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51441
51473
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: /* @__PURE__ */ jsxRuntime.jsx(Timer2, {}) }),
51442
51474
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-300", children: "|" }),
51443
51475
  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() }),
51476
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75", children: getShiftIcon2() }),
51445
51477
  getShiftName()
51446
51478
  ] })
51447
51479
  ] })
@@ -51458,7 +51490,7 @@ var DashboardHeader = React141.memo(({ lineTitle, className = "", headerControls
51458
51490
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 inline-flex flex-wrap items-center gap-3", children: [
51459
51491
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs md:text-sm font-medium text-gray-600 whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx(Timer2, {}) }),
51460
51492
  /* @__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() }),
51493
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon2() }),
51462
51494
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs md:text-sm font-medium text-gray-600 whitespace-nowrap", children: getShiftName() })
51463
51495
  ] }) })
51464
51496
  ] })
@@ -58429,7 +58461,7 @@ var FactoryView = ({
58429
58461
  const currentShift = getCurrentShiftInfo();
58430
58462
  return (currentShift.shiftName || "Day").replace(/ Shift$/i, "");
58431
58463
  };
58432
- const getShiftIcon = () => {
58464
+ const getShiftIcon2 = () => {
58433
58465
  const currentShift = getCurrentShiftInfo();
58434
58466
  const shiftNameLower = (currentShift.shiftName || "").toLowerCase();
58435
58467
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || currentShift.shiftId === 0) {
@@ -58475,7 +58507,7 @@ var FactoryView = ({
58475
58507
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center justify-center gap-2", children: [
58476
58508
  /* @__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
58509
  /* @__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() }),
58510
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2() }),
58479
58511
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-gray-700", children: [
58480
58512
  getShiftName(),
58481
58513
  " Shift"
@@ -58494,7 +58526,7 @@ var FactoryView = ({
58494
58526
  " IST"
58495
58527
  ] }),
58496
58528
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1", children: [
58497
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon() }),
58529
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-600", children: getShiftIcon2() }),
58498
58530
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-gray-600", children: [
58499
58531
  getShiftName(),
58500
58532
  " Shift"
@@ -61206,7 +61238,7 @@ var KPIDetailView = ({
61206
61238
  const getShiftName = React141.useCallback((shiftId) => {
61207
61239
  return getShiftNameById(shiftId, configuredTimezone, shiftConfig);
61208
61240
  }, [configuredTimezone, shiftConfig]);
61209
- const getShiftIcon = React141.useCallback((shiftId) => {
61241
+ const getShiftIcon2 = React141.useCallback((shiftId) => {
61210
61242
  if (shiftId === 0) {
61211
61243
  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
61244
  }
@@ -62086,7 +62118,7 @@ var KPIDetailView = ({
62086
62118
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
62087
62119
  /* @__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
62120
  /* @__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) }),
62121
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2(chartMetrics.shift_id ?? 0) }),
62090
62122
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-gray-700", children: [
62091
62123
  getShiftName(chartMetrics.shift_id ?? 0).replace(/ Shift$/i, ""),
62092
62124
  " Shift"
@@ -62112,7 +62144,7 @@ var KPIDetailView = ({
62112
62144
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
62113
62145
  ] }),
62114
62146
  /* @__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) }),
62147
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(chartMetrics.shift_id ?? 0) }),
62116
62148
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
62117
62149
  getShiftName(chartMetrics.shift_id ?? 0).replace(/ Shift$/i, ""),
62118
62150
  " Shift"
@@ -62130,7 +62162,7 @@ var KPIDetailView = ({
62130
62162
  return `${startDate.toLocaleDateString("en-US", { month: "short", day: "numeric" })} - ${endDate.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" })}`;
62131
62163
  })() }) }),
62132
62164
  /* @__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) }),
62165
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2(selectedShiftId) }),
62134
62166
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: getShiftName(selectedShiftId) })
62135
62167
  ] })
62136
62168
  ] }),
@@ -62147,7 +62179,7 @@ var KPIDetailView = ({
62147
62179
  ] }),
62148
62180
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
62149
62181
  /* @__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) }),
62182
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(selectedShiftId) }),
62151
62183
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
62152
62184
  getShiftName(selectedShiftId).replace(/ Shift$/i, ""),
62153
62185
  " Shift"
@@ -63523,7 +63555,7 @@ var KPIsOverviewView = ({
63523
63555
  const headerShiftId = showHistoricalLeaderboardHeader ? effectiveLeaderboardShiftId : currentShiftDetails.shiftId;
63524
63556
  const headerShiftName = getShiftNameById(headerShiftId, configuredTimezone, shiftConfig).replace(/ Shift$/i, "");
63525
63557
  const headerDateLabel = isMonthlyMode ? getMonthRange() : formatLocalDate2(headerDateKey);
63526
- const getShiftIcon = (shiftId) => {
63558
+ const getShiftIcon2 = (shiftId) => {
63527
63559
  const shiftNameLower = getShiftNameById(shiftId, configuredTimezone, shiftConfig).toLowerCase();
63528
63560
  if (shiftNameLower.includes("day") || shiftNameLower.includes("morning") || shiftId === 0) {
63529
63561
  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 +63672,7 @@ var KPIsOverviewView = ({
63640
63672
  /* @__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
63673
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63642
63674
  /* @__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) }),
63675
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2(headerShiftId) }),
63644
63676
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: headerShiftName })
63645
63677
  ] }),
63646
63678
  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 +63868,7 @@ var KPIsOverviewView = ({
63836
63868
  !isMonthlyMode && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
63837
63869
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" }),
63838
63870
  /* @__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) }),
63871
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(headerShiftId) }),
63840
63872
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-semibold uppercase tracking-wider", children: [
63841
63873
  headerShiftName,
63842
63874
  " Shift"
@@ -63980,6 +64012,52 @@ var KPIsOverviewView = ({
63980
64012
  ] });
63981
64013
  };
63982
64014
  var KPIsOverviewView_default = KPIsOverviewView;
64015
+ var toFiniteNumber = (value) => {
64016
+ if (typeof value === "number" && Number.isFinite(value)) return value;
64017
+ if (typeof value === "string" && value.trim() !== "") {
64018
+ const parsed = Number(value);
64019
+ return Number.isFinite(parsed) ? parsed : null;
64020
+ }
64021
+ return null;
64022
+ };
64023
+ var getCycleRatio = (workspace) => {
64024
+ const idealCycleTime = toFiniteNumber(workspace.ideal_cycle_time);
64025
+ const avgCycleTime = toFiniteNumber(workspace.avg_cycle_time);
64026
+ if (idealCycleTime === null || avgCycleTime === null || idealCycleTime <= 0 || avgCycleTime <= 0) {
64027
+ return null;
64028
+ }
64029
+ return idealCycleTime / avgCycleTime;
64030
+ };
64031
+ var formatCycleTimeValue = (value) => {
64032
+ const numericValue = toFiniteNumber(value);
64033
+ if (numericValue === null || numericValue <= 0) return "--";
64034
+ return `${numericValue.toFixed(1)}s`;
64035
+ };
64036
+ var CycleTimeComparison = React141.memo(({
64037
+ workspace,
64038
+ variant = "table"
64039
+ }) => {
64040
+ const averageValue = formatCycleTimeValue(workspace.avg_cycle_time);
64041
+ const standardValue = formatCycleTimeValue(workspace.ideal_cycle_time);
64042
+ if (variant === "mobile") {
64043
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center justify-between py-2 border-t border-gray-100", children: [
64044
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Standard Cycle Time" }),
64045
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium tabular-nums text-gray-500", children: standardValue })
64046
+ ] });
64047
+ }
64048
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
64049
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
64050
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wider text-gray-500", children: "Average" }),
64051
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold tabular-nums text-gray-900", children: averageValue })
64052
+ ] }),
64053
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-px bg-gray-200" }),
64054
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
64055
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-medium uppercase tracking-wider text-gray-500", children: "Standard" }),
64056
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium tabular-nums text-gray-500", children: standardValue })
64057
+ ] })
64058
+ ] });
64059
+ }, (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);
64060
+ CycleTimeComparison.displayName = "CycleTimeComparison";
63983
64061
  var IsolatedTimer = React141.memo(() => {
63984
64062
  return /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {});
63985
64063
  });
@@ -63997,11 +64075,11 @@ var HeaderRibbon = React141.memo(({
63997
64075
  currentDate,
63998
64076
  currentMobileDate,
63999
64077
  shiftId,
64000
- getShiftIcon,
64078
+ getShiftIcon: getShiftIcon2,
64001
64079
  getShiftName,
64002
64080
  showTimer = true
64003
64081
  }) => {
64004
- const shiftIcon = React141.useMemo(() => getShiftIcon(shiftId), [getShiftIcon, shiftId]);
64082
+ const shiftIcon = React141.useMemo(() => getShiftIcon2(shiftId), [getShiftIcon2, shiftId]);
64005
64083
  const shiftName = React141.useMemo(() => getShiftName(shiftId), [getShiftName, shiftId]);
64006
64084
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
64007
64085
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
@@ -64043,8 +64121,9 @@ var MobileWorkspaceCard = React141.memo(({
64043
64121
  isClickable,
64044
64122
  onWorkspaceClick,
64045
64123
  getMedalIcon,
64046
- efficiencyLabel
64047
- }) => /* @__PURE__ */ jsxRuntime.jsx(
64124
+ metricLabel,
64125
+ isAssemblyMode
64126
+ }) => /* @__PURE__ */ jsxRuntime.jsxs(
64048
64127
  motion.div,
64049
64128
  {
64050
64129
  layout: true,
@@ -64055,28 +64134,31 @@ var MobileWorkspaceCard = React141.memo(({
64055
64134
  },
64056
64135
  onClick: isClickable ? () => onWorkspaceClick(workspace, rank) : void 0,
64057
64136
  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
64137
+ children: [
64138
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2 gap-3", children: [
64139
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
64140
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
64141
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-bold text-gray-700", children: [
64142
+ "#",
64143
+ rank
64144
+ ] }),
64145
+ getMedalIcon(rank)
64064
64146
  ] }),
64065
- getMedalIcon(rank)
64147
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
64148
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children: workspace.displayName }),
64149
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: workspace.lineName })
64150
+ ] })
64066
64151
  ] }),
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 })
64152
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
64153
+ /* @__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 }) }),
64154
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: metricLabel })
64070
64155
  ] })
64071
64156
  ] }),
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
- ] })
64157
+ isAssemblyMode && /* @__PURE__ */ jsxRuntime.jsx(CycleTimeComparison, { workspace, variant: "mobile" })
64158
+ ]
64077
64159
  }
64078
64160
  ), (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;
64161
+ 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
64162
  });
64081
64163
  MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
64082
64164
  var DesktopWorkspaceRow = React141.memo(({
@@ -64085,7 +64167,8 @@ var DesktopWorkspaceRow = React141.memo(({
64085
64167
  rowClass,
64086
64168
  isClickable,
64087
64169
  onWorkspaceClick,
64088
- getMedalIcon
64170
+ getMedalIcon,
64171
+ isAssemblyMode
64089
64172
  }) => /* @__PURE__ */ jsxRuntime.jsxs(
64090
64173
  motion.tr,
64091
64174
  {
@@ -64102,11 +64185,11 @@ var DesktopWorkspaceRow = React141.memo(({
64102
64185
  ] }) }),
64103
64186
  /* @__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
64187
  /* @__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 }) })
64188
+ /* @__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
64189
  ]
64107
64190
  }
64108
64191
  ), (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;
64192
+ 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
64193
  });
64111
64194
  DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
64112
64195
  var LeaderboardDetailView = React141.memo(({
@@ -64128,6 +64211,7 @@ var LeaderboardDetailView = React141.memo(({
64128
64211
  const supabase = useSupabase();
64129
64212
  const [sortAscending, setSortAscending] = React141.useState(false);
64130
64213
  const [viewType, setViewType] = React141.useState("operator");
64214
+ const [outputCategory, setOutputCategory] = React141.useState("standard");
64131
64215
  const [activeTab, setActiveTab] = React141.useState("today");
64132
64216
  const timezone = useAppTimezone();
64133
64217
  const staticShiftConfig = useShiftConfig();
@@ -64206,6 +64290,7 @@ var LeaderboardDetailView = React141.memo(({
64206
64290
  const monthlyRequestKeyRef = React141.useRef(null);
64207
64291
  const leaderboardUpdateQueuedRef = React141.useRef(false);
64208
64292
  const leaderboardUpdateTimerRef = React141.useRef(null);
64293
+ const leaderboardViewTrackedRef = React141.useRef(null);
64209
64294
  const filterRef = React141.useRef(null);
64210
64295
  const filterButtonRef = React141.useRef(null);
64211
64296
  const mobileFilterButtonRef = React141.useRef(null);
@@ -64338,6 +64423,17 @@ var LeaderboardDetailView = React141.memo(({
64338
64423
  }
64339
64424
  return Array.from(uniqueLines.entries()).map(([id3, name]) => ({ id: id3, label: name }));
64340
64425
  }, [configuredLineIds, lines, getLineName]);
64426
+ const scopedLines = React141.useMemo(() => {
64427
+ const configuredLineIdSet = new Set(configuredLineIds);
64428
+ return lines.filter((line) => configuredLineIds.length === 0 || configuredLineIdSet.has(line.id));
64429
+ }, [configuredLineIds, lines]);
64430
+ const scopedLineAssemblyMap = React141.useMemo(() => {
64431
+ const map = /* @__PURE__ */ new Map();
64432
+ scopedLines.forEach((line) => {
64433
+ map.set(line.id, line.assembly === true);
64434
+ });
64435
+ return map;
64436
+ }, [scopedLines]);
64341
64437
  const shiftOptions = React141.useMemo(() => {
64342
64438
  if (activeShiftConfig?.shifts && activeShiftConfig.shifts.length > 0) {
64343
64439
  return activeShiftConfig.shifts.map((shift2) => ({
@@ -64361,6 +64457,33 @@ var LeaderboardDetailView = React141.memo(({
64361
64457
  const parsed = Number(selectedShiftFilter);
64362
64458
  return Number.isFinite(parsed) ? parsed : void 0;
64363
64459
  }, [selectedShiftFilter]);
64460
+ const outputScopeLines = React141.useMemo(() => {
64461
+ const outputLines = scopedLines.filter((line) => (line.monitoring_mode ?? "output") !== "uptime");
64462
+ if (selectedLineFilter === "all") {
64463
+ return outputLines;
64464
+ }
64465
+ return outputLines.filter((line) => line.id === selectedLineFilter);
64466
+ }, [scopedLines, selectedLineFilter]);
64467
+ const hasAssemblyOutputScope = React141.useMemo(
64468
+ () => outputScopeLines.some((line) => line.assembly === true),
64469
+ [outputScopeLines]
64470
+ );
64471
+ const hasStandardOutputScope = React141.useMemo(
64472
+ () => outputScopeLines.some((line) => line.assembly !== true),
64473
+ [outputScopeLines]
64474
+ );
64475
+ const showOutputCategoryDropdown = viewType === "operator" && hasAssemblyOutputScope && hasStandardOutputScope;
64476
+ const isAssemblyMode = viewType === "operator" && outputCategory === "assembly";
64477
+ React141.useEffect(() => {
64478
+ if (viewType !== "operator") return;
64479
+ if (hasAssemblyOutputScope && !hasStandardOutputScope && outputCategory !== "assembly") {
64480
+ setOutputCategory("assembly");
64481
+ return;
64482
+ }
64483
+ if (hasStandardOutputScope && !hasAssemblyOutputScope && outputCategory !== "standard") {
64484
+ setOutputCategory("standard");
64485
+ }
64486
+ }, [viewType, hasAssemblyOutputScope, hasStandardOutputScope, outputCategory]);
64364
64487
  const handleLineFilterChange = React141.useCallback((value) => {
64365
64488
  setSelectedLineFilter(value);
64366
64489
  }, []);
@@ -64370,17 +64493,19 @@ var LeaderboardDetailView = React141.memo(({
64370
64493
  const clearFilters = React141.useCallback(() => {
64371
64494
  setSortAscending(false);
64372
64495
  setSelectedLineFilter("all");
64496
+ setOutputCategory("standard");
64373
64497
  setSelectedShiftFilter(currentShiftInfo.shiftId.toString());
64374
64498
  }, [currentShiftInfo.shiftId]);
64375
64499
  const activeFiltersCount = React141.useMemo(() => {
64376
64500
  let count = 0;
64377
64501
  if (sortAscending) count++;
64378
64502
  if (selectedLineFilter !== "all") count++;
64503
+ if (showOutputCategoryDropdown && outputCategory !== "standard") count++;
64379
64504
  if (selectedShiftFilter !== currentShiftInfo.shiftId.toString()) {
64380
64505
  count++;
64381
64506
  }
64382
64507
  return count;
64383
- }, [sortAscending, selectedLineFilter, selectedShiftFilter, currentShiftInfo.shiftId]);
64508
+ }, [sortAscending, selectedLineFilter, outputCategory, showOutputCategoryDropdown, selectedShiftFilter, currentShiftInfo.shiftId]);
64384
64509
  const shouldFetchShiftConfigs = !date && shiftId === void 0;
64385
64510
  const {
64386
64511
  shiftConfigMap: multiLineShiftConfigMap,
@@ -64432,7 +64557,8 @@ var LeaderboardDetailView = React141.memo(({
64432
64557
  action_count: entry.total_output || 0,
64433
64558
  pph: entry.avg_pph || 0,
64434
64559
  performance_score: entry.performance_score ?? 0,
64435
- avg_cycle_time: entry.avg_cycle_time || 0,
64560
+ avg_cycle_time: toFiniteNumber(entry.avg_cycle_time) ?? 0,
64561
+ ideal_cycle_time: toFiniteNumber(entry.ideal_cycle_time) ?? void 0,
64436
64562
  trend: 0,
64437
64563
  predicted_output: 0,
64438
64564
  efficiency: entry.efficiency || 0,
@@ -64610,7 +64736,7 @@ var LeaderboardDetailView = React141.memo(({
64610
64736
  const currentShift = getCurrentShift(timezone || "Asia/Kolkata", activeShiftConfig);
64611
64737
  return currentShift.shiftName || getShiftNameById(currentShift.shiftId, timezone || "Asia/Kolkata", activeShiftConfig);
64612
64738
  }, [timezone, activeShiftConfig, shiftGroups]);
64613
- const getShiftIcon = React141.useCallback((shiftId2) => {
64739
+ const getShiftIcon2 = React141.useCallback((shiftId2) => {
64614
64740
  if (shiftId2 === void 0 && shiftGroups.length > 1) {
64615
64741
  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
64742
  }
@@ -64676,15 +64802,20 @@ var LeaderboardDetailView = React141.memo(({
64676
64802
  if (!canOpenWorkspace(workspace.line_id)) {
64677
64803
  return;
64678
64804
  }
64805
+ const cycleRatio = getCycleRatio(workspace);
64679
64806
  trackCoreEvent("Workspace from Leaderboard Clicked", {
64680
64807
  workspace_name: workspace.workspace_name,
64681
64808
  workspace_id: workspace.workspace_uuid,
64682
64809
  rank,
64683
64810
  total_workspaces: workspacesLengthRef.current,
64684
64811
  // Use ref instead of state to avoid dependency
64812
+ metric_context: viewType === "machine" ? "machine" : outputCategory,
64685
64813
  efficiency: workspace.efficiency,
64686
64814
  action_count: workspace.action_count,
64687
- action_threshold: workspace.action_threshold
64815
+ action_threshold: workspace.action_threshold,
64816
+ avg_cycle_time: workspace.avg_cycle_time,
64817
+ ideal_cycle_time: workspace.ideal_cycle_time ?? null,
64818
+ cycle_ratio: cycleRatio
64688
64819
  });
64689
64820
  const displayName = workspace.displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
64690
64821
  const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName, workspace.line_id) : "";
@@ -64708,7 +64839,7 @@ var LeaderboardDetailView = React141.memo(({
64708
64839
  const combinedParams = navParams ? `${navParams}&${contextParamString}` : `?${contextParamString}`;
64709
64840
  navigation.navigate(`/workspace/${workspace.workspace_uuid}${combinedParams}`);
64710
64841
  }
64711
- }, [canOpenWorkspace, onWorkspaceClick, navigation, date, shiftId]);
64842
+ }, [canOpenWorkspace, onWorkspaceClick, navigation, date, shiftId, outputCategory, viewType]);
64712
64843
  React141.useEffect(() => {
64713
64844
  workspacesLengthRef.current = activeEntries.length || 0;
64714
64845
  }, [activeEntries.length]);
@@ -64729,16 +64860,20 @@ var LeaderboardDetailView = React141.memo(({
64729
64860
  return activeEntries.map((ws) => ({
64730
64861
  ...ws,
64731
64862
  displayName: ws.displayName || getWorkspaceDisplayName(ws.workspace_name, ws.line_id),
64732
- lineName: getLineName(ws.line_id)
64863
+ lineName: getLineName(ws.line_id),
64864
+ isAssemblyLine: scopedLineAssemblyMap.get(ws.line_id) === true
64733
64865
  }));
64734
- }, [activeEntries, getLineName]);
64866
+ }, [activeEntries, getLineName, scopedLineAssemblyMap]);
64735
64867
  const sortedWorkspaces = React141.useMemo(() => {
64736
64868
  let filtered = [...workspaceDisplayData];
64737
64869
  filtered = filtered.filter((ws) => {
64738
64870
  if (viewType === "machine") {
64739
64871
  return ws.monitoring_mode === "uptime";
64740
64872
  }
64741
- return ws.monitoring_mode !== "uptime";
64873
+ if (ws.monitoring_mode === "uptime") {
64874
+ return false;
64875
+ }
64876
+ return isAssemblyMode ? ws.isAssemblyLine : !ws.isAssemblyLine;
64742
64877
  });
64743
64878
  if (selectedLineFilter !== "all") {
64744
64879
  filtered = filtered.filter((ws) => ws.line_id === selectedLineFilter);
@@ -64747,13 +64882,61 @@ var LeaderboardDetailView = React141.memo(({
64747
64882
  filtered = filtered.filter((ws) => ws.shift_id?.toString() === selectedShiftFilter);
64748
64883
  }
64749
64884
  return filtered.sort((a, b) => {
64885
+ if (isAssemblyMode) {
64886
+ const ratioA = getCycleRatio(a);
64887
+ const ratioB = getCycleRatio(b);
64888
+ if (ratioA === null && ratioB === null) return 0;
64889
+ if (ratioA === null) return 1;
64890
+ if (ratioB === null) return -1;
64891
+ return sortAscending ? ratioA - ratioB : ratioB - ratioA;
64892
+ }
64750
64893
  const effA = a.efficiency || 0;
64751
64894
  const effB = b.efficiency || 0;
64752
64895
  return sortAscending ? effA - effB : effB - effA;
64753
64896
  });
64754
- }, [workspaceDisplayData, sortAscending, selectedLineFilter, selectedShiftFilter, activeTab, viewType]);
64897
+ }, [workspaceDisplayData, sortAscending, selectedLineFilter, selectedShiftFilter, activeTab, viewType, isAssemblyMode]);
64755
64898
  const loading = activeTab === "today" ? todayLoading : monthlyLoading;
64756
64899
  const error = activeTab === "today" ? todayError : monthlyError;
64900
+ React141.useEffect(() => {
64901
+ if (loading || error || sortedWorkspaces.length === 0) return;
64902
+ const trackingKey = [
64903
+ activeTab,
64904
+ viewType,
64905
+ outputCategory,
64906
+ selectedLineFilter,
64907
+ selectedShiftFilter,
64908
+ sortAscending ? "asc" : "desc",
64909
+ sortedWorkspaces.length
64910
+ ].join("|");
64911
+ if (leaderboardViewTrackedRef.current === trackingKey) return;
64912
+ leaderboardViewTrackedRef.current = trackingKey;
64913
+ const topWorkspace = sortedWorkspaces[0];
64914
+ trackCoreEvent("Workspace Leaderboard View Loaded", {
64915
+ time_range: activeTab,
64916
+ view_type: viewType,
64917
+ metric_context: viewType === "machine" ? "machine" : outputCategory,
64918
+ line_filter: selectedLineFilter,
64919
+ shift_filter: selectedShiftFilter,
64920
+ sort_direction: sortAscending ? "asc" : "desc",
64921
+ workspace_count: sortedWorkspaces.length,
64922
+ top_workspace_id: topWorkspace?.workspace_uuid ?? null,
64923
+ top_workspace_name: topWorkspace?.workspace_name ?? null,
64924
+ top_efficiency: topWorkspace?.efficiency ?? null,
64925
+ top_avg_cycle_time: topWorkspace?.avg_cycle_time ?? null,
64926
+ top_ideal_cycle_time: topWorkspace?.ideal_cycle_time ?? null,
64927
+ top_cycle_ratio: topWorkspace ? getCycleRatio(topWorkspace) : null
64928
+ });
64929
+ }, [
64930
+ loading,
64931
+ error,
64932
+ sortedWorkspaces,
64933
+ activeTab,
64934
+ viewType,
64935
+ outputCategory,
64936
+ selectedLineFilter,
64937
+ selectedShiftFilter,
64938
+ sortAscending
64939
+ ]);
64757
64940
  const currentDateFormatted = React141.useMemo(() => {
64758
64941
  const dateStr = (/* @__PURE__ */ new Date()).toDateString();
64759
64942
  return formatDate2(new Date(dateStr));
@@ -64771,7 +64954,9 @@ var LeaderboardDetailView = React141.memo(({
64771
64954
  error.message
64772
64955
  ] }) });
64773
64956
  }
64774
- const efficiencyLabel = viewType === "machine" ? "Utilization" : "Efficiency";
64957
+ const metricLabel = viewType === "machine" ? "Utilization" : isAssemblyMode ? "Cycle Time" : "Efficiency";
64958
+ const descendingSortLabel = "Highest to Lowest";
64959
+ const ascendingSortLabel = "Lowest to Highest";
64775
64960
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, style: { willChange: "contents" }, children: [
64776
64961
  /* @__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
64962
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
@@ -64821,7 +65006,7 @@ var LeaderboardDetailView = React141.memo(({
64821
65006
  ] }),
64822
65007
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
64823
65008
  /* @__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 }),
65009
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: metricLabel }),
64825
65010
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
64826
65011
  "select",
64827
65012
  {
@@ -64830,8 +65015,25 @@ var LeaderboardDetailView = React141.memo(({
64830
65015
  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
65016
  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
65017
  children: [
64833
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "desc", children: "Highest to Lowest" }),
64834
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "asc", children: "Lowest to Highest" })
65018
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "desc", children: descendingSortLabel }),
65019
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "asc", children: ascendingSortLabel })
65020
+ ]
65021
+ }
65022
+ ) })
65023
+ ] }),
65024
+ showOutputCategoryDropdown && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 sm:hidden", children: [
65025
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide ml-1", children: "Output Type" }),
65026
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
65027
+ "select",
65028
+ {
65029
+ "aria-label": "Output leaderboard category",
65030
+ value: outputCategory,
65031
+ onChange: (e) => setOutputCategory(e.target.value),
65032
+ 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",
65033
+ 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` },
65034
+ children: [
65035
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "standard", children: "Standard" }),
65036
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", children: "Assembly" })
64835
65037
  ]
64836
65038
  }
64837
65039
  ) })
@@ -64873,7 +65075,7 @@ var LeaderboardDetailView = React141.memo(({
64873
65075
  currentDate: activeTab === "monthly" ? monthlyRangeText : currentDateFormatted,
64874
65076
  currentMobileDate: activeTab === "monthly" ? monthlyRangeText : currentMobileDateFormatted,
64875
65077
  shiftId: activeTab === "monthly" ? monthlyShiftId : todayShiftId,
64876
- getShiftIcon,
65078
+ getShiftIcon: getShiftIcon2,
64877
65079
  getShiftName,
64878
65080
  showTimer: activeTab === "today"
64879
65081
  }
@@ -64971,6 +65173,20 @@ var LeaderboardDetailView = React141.memo(({
64971
65173
  ]
64972
65174
  }
64973
65175
  ) }),
65176
+ showOutputCategoryDropdown && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsxs(
65177
+ "select",
65178
+ {
65179
+ "aria-label": "Output leaderboard category",
65180
+ value: outputCategory,
65181
+ onChange: (e) => setOutputCategory(e.target.value),
65182
+ 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",
65183
+ 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` },
65184
+ children: [
65185
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "standard", children: "Standard" }),
65186
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "assembly", children: "Assembly" })
65187
+ ]
65188
+ }
65189
+ ) }),
64974
65190
  /* @__PURE__ */ jsxRuntime.jsxs(
64975
65191
  "button",
64976
65192
  {
@@ -65002,7 +65218,8 @@ var LeaderboardDetailView = React141.memo(({
65002
65218
  isClickable: canOpenWorkspace(ws.line_id),
65003
65219
  onWorkspaceClick: stableHandleWorkspaceClick,
65004
65220
  getMedalIcon: stableGetMedalIcon,
65005
- efficiencyLabel
65221
+ metricLabel,
65222
+ isAssemblyMode
65006
65223
  },
65007
65224
  ws.workspace_uuid
65008
65225
  );
@@ -65013,7 +65230,7 @@ var LeaderboardDetailView = React141.memo(({
65013
65230
  /* @__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
65231
  /* @__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
65232
  /* @__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 })
65233
+ /* @__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
65234
  ] }) }),
65018
65235
  /* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-gray-100", children: sortedWorkspaces.map((ws, index) => {
65019
65236
  const isTopThree = index < 3;
@@ -65026,7 +65243,8 @@ var LeaderboardDetailView = React141.memo(({
65026
65243
  rowClass,
65027
65244
  isClickable: canOpenWorkspace(ws.line_id),
65028
65245
  onWorkspaceClick: stableHandleWorkspaceClick,
65029
- getMedalIcon: stableGetMedalIcon
65246
+ getMedalIcon: stableGetMedalIcon,
65247
+ isAssemblyMode
65030
65248
  },
65031
65249
  ws.workspace_uuid
65032
65250
  );
@@ -65044,7 +65262,10 @@ function LoginView({
65044
65262
  logoSrc = optifye_logo_default,
65045
65263
  logoAlt = "Optifye",
65046
65264
  brandName = "Optifye",
65047
- onRateLimitCheck
65265
+ onRateLimitCheck,
65266
+ showDevTestLogin = false,
65267
+ devTestLoginLabel,
65268
+ onDevTestLogin
65048
65269
  }) {
65049
65270
  return /* @__PURE__ */ jsxRuntime.jsx(
65050
65271
  LoginPage,
@@ -65052,7 +65273,10 @@ function LoginView({
65052
65273
  logoSrc,
65053
65274
  logoAlt,
65054
65275
  brandName,
65055
- onRateLimitCheck
65276
+ onRateLimitCheck,
65277
+ showDevTestLogin,
65278
+ devTestLoginLabel,
65279
+ onDevTestLogin
65056
65280
  }
65057
65281
  );
65058
65282
  }
@@ -68240,20 +68464,30 @@ function useTimezone(options = {}) {
68240
68464
  }
68241
68465
 
68242
68466
  // src/views/workspace-detail-view.utils.ts
68243
- var getWorkspaceCycleTimePresentation = ({
68467
+ var getWorkspaceDetailLayoutMode = ({
68244
68468
  workspace,
68245
68469
  showCycleTimeChart
68246
68470
  }) => {
68247
68471
  if (workspace?.monitoring_mode === "uptime") {
68248
- return "output";
68472
+ return "uptime";
68249
68473
  }
68250
68474
  if (showCycleTimeChart === false) {
68251
68475
  return "output";
68252
68476
  }
68253
- if (!shouldUseAssemblyCycleTimeLayout(workspace)) {
68477
+ return shouldUseAssemblyCycleTimeLayout(workspace) ? "assembly_cycle" : "output";
68478
+ };
68479
+ var getCycleTimeRenderState = ({
68480
+ workspace,
68481
+ authoritativeMetrics,
68482
+ showCycleTimeChart
68483
+ }) => {
68484
+ if (getWorkspaceDetailLayoutMode({ workspace, showCycleTimeChart }) !== "assembly_cycle") {
68254
68485
  return "output";
68255
68486
  }
68256
- return workspace?.cycle_time_data_status === "missing_clips" ? "cycle_unavailable" : "cycle_chart";
68487
+ if (!authoritativeMetrics) {
68488
+ return "chart_loading";
68489
+ }
68490
+ return authoritativeMetrics.cycle_time_data_status === "missing_clips" ? "cycle_unavailable" : "cycle_chart";
68257
68491
  };
68258
68492
  var formatDateInTimezone = (date = /* @__PURE__ */ new Date(), timezone, options) => {
68259
68493
  const defaultOptions = {
@@ -68661,33 +68895,34 @@ var WorkspaceDetailView = ({
68661
68895
  idle_time_hourly: void 0
68662
68896
  };
68663
68897
  }, [cachedOverviewMetrics, shiftConfig?.shifts]);
68664
- const workspace = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics || overviewFallback;
68898
+ const authoritativeCycleMetrics = isHistoricView ? historicMetrics : liveMetrics;
68899
+ const workspace = authoritativeCycleMetrics || cachedDetailedMetrics || overviewFallback;
68665
68900
  const { timezone: cycleTimeTimezone } = useTimezone({
68666
68901
  lineId: effectiveLineId || workspace?.line_id || void 0,
68667
68902
  workspaceId: workspaceId || void 0
68668
68903
  });
68669
68904
  const effectiveCycleTimeTimezone = cycleTimeTimezone || timezone;
68670
- const detailedWorkspaceMetrics = (isHistoricView ? historicMetrics : liveMetrics) || cachedDetailedMetrics;
68905
+ const detailedWorkspaceMetrics = authoritativeCycleMetrics || cachedDetailedMetrics;
68671
68906
  const cycleTimeChartData = React141.useMemo(
68672
- () => Array.isArray(workspace?.hourly_cycle_times) ? workspace.hourly_cycle_times.map((value) => {
68907
+ () => Array.isArray(authoritativeCycleMetrics?.hourly_cycle_times) ? authoritativeCycleMetrics.hourly_cycle_times.map((value) => {
68673
68908
  const numericValue = Number(value);
68674
68909
  return Number.isFinite(numericValue) ? numericValue : 0;
68675
68910
  }) : [],
68676
- [workspace?.hourly_cycle_times]
68911
+ [authoritativeCycleMetrics?.hourly_cycle_times]
68677
68912
  );
68678
68913
  const maskedCycleTimeChartData = React141.useMemo(
68679
68914
  () => maskFutureHourlySeries({
68680
68915
  data: cycleTimeChartData,
68681
- shiftStart: workspace?.shift_start,
68682
- shiftEnd: workspace?.shift_end,
68683
- shiftDate: workspace?.date || date || calculatedOperationalDate || null,
68916
+ shiftStart: authoritativeCycleMetrics?.shift_start,
68917
+ shiftEnd: authoritativeCycleMetrics?.shift_end,
68918
+ shiftDate: authoritativeCycleMetrics?.date || date || calculatedOperationalDate || null,
68684
68919
  timezone: effectiveCycleTimeTimezone
68685
68920
  }),
68686
68921
  [
68687
68922
  cycleTimeChartData,
68688
- workspace?.shift_start,
68689
- workspace?.shift_end,
68690
- workspace?.date,
68923
+ authoritativeCycleMetrics?.shift_start,
68924
+ authoritativeCycleMetrics?.shift_end,
68925
+ authoritativeCycleMetrics?.date,
68691
68926
  date,
68692
68927
  calculatedOperationalDate,
68693
68928
  effectiveCycleTimeTimezone
@@ -68695,13 +68930,13 @@ var WorkspaceDetailView = ({
68695
68930
  );
68696
68931
  const cycleTimeDatasetKey = React141.useMemo(
68697
68932
  () => [
68698
- workspace?.workspace_id || workspaceId || "workspace",
68699
- date || workspace?.date || "live",
68700
- parsedShiftId ?? workspace?.shift_id ?? "current",
68933
+ authoritativeCycleMetrics?.workspace_id || workspaceId || "workspace",
68934
+ date || authoritativeCycleMetrics?.date || "live",
68935
+ parsedShiftId ?? authoritativeCycleMetrics?.shift_id ?? "current",
68701
68936
  "hourly",
68702
68937
  "backend"
68703
68938
  ].join(":"),
68704
- [workspace?.workspace_id, workspaceId, date, workspace?.date, parsedShiftId, workspace?.shift_id]
68939
+ [authoritativeCycleMetrics?.workspace_id, workspaceId, date, authoritativeCycleMetrics?.date, parsedShiftId, authoritativeCycleMetrics?.shift_id]
68705
68940
  );
68706
68941
  const hasWorkspaceSnapshot = Boolean(workspace);
68707
68942
  const loading = ((isHistoricView ? historicLoading : liveLoading) || isShiftConfigLoading) && !hasWorkspaceSnapshot;
@@ -68950,30 +69185,46 @@ var WorkspaceDetailView = ({
68950
69185
  action_type: workspace.action_type
68951
69186
  } : null;
68952
69187
  const isAssemblyWorkspace = shouldUseAssemblyCycleTimeLayout(workspaceCycleTimeEligibility);
68953
- const cycleTimePresentation = getWorkspaceCycleTimePresentation({
69188
+ const layoutMode = getWorkspaceDetailLayoutMode({
68954
69189
  workspace: workspace ? {
68955
69190
  monitoring_mode: workspace.monitoring_mode,
68956
69191
  line_assembly_enabled: workspace.line_assembly_enabled,
68957
69192
  action_family: workspace.action_family,
68958
- action_type: workspace.action_type,
68959
- cycle_time_data_status: workspace.cycle_time_data_status
69193
+ action_type: workspace.action_type
68960
69194
  } : null,
68961
69195
  showCycleTimeChart
68962
69196
  });
68963
- const shouldShowCycleTimeChart = cycleTimePresentation !== "output";
69197
+ const isAssemblyCycleLayout = layoutMode === "assembly_cycle";
69198
+ const isOutputLayout = layoutMode === "output";
69199
+ const cycleTimePresentation = getCycleTimeRenderState({
69200
+ workspace: workspace ? {
69201
+ monitoring_mode: workspace.monitoring_mode,
69202
+ line_assembly_enabled: workspace.line_assembly_enabled,
69203
+ action_family: workspace.action_family,
69204
+ action_type: workspace.action_type
69205
+ } : null,
69206
+ authoritativeMetrics: authoritativeCycleMetrics ? {
69207
+ cycle_time_data_status: authoritativeCycleMetrics.cycle_time_data_status
69208
+ } : null,
69209
+ showCycleTimeChart
69210
+ });
69211
+ const shouldShowCycleTimeChart = cycleTimePresentation === "cycle_chart";
68964
69212
  const shouldShowCycleTimeUnavailableState = cycleTimePresentation === "cycle_unavailable";
68965
- const showIdleBreakdownChart = !shouldShowCycleTimeChart && idleTimeVlmEnabled;
69213
+ const shouldShowCycleTimeLoadingState = cycleTimePresentation === "chart_loading";
69214
+ const shouldShowAssemblyOverviewLoadingState = isAssemblyCycleLayout && shouldShowCycleTimeLoadingState && hasWorkspaceSnapshot;
69215
+ const showIdleBreakdownChart = !isAssemblyCycleLayout && idleTimeVlmEnabled;
69216
+ const canToggleChartIdleTime = !isUptimeMode && !shouldShowCycleTimeLoadingState && !shouldShowCycleTimeUnavailableState;
68966
69217
  const idleClipDate = date || workspace?.date || calculatedOperationalDate || getOperationalDate(timezone);
68967
69218
  const idleClipShiftId = parsedShiftId ?? workspace?.shift_id;
68968
69219
  const rawHourlyIdleMinutes = React141.useMemo(() => {
68969
69220
  if (!shouldShowCycleTimeChart || !workspace?.idle_time_hourly || !workspace?.shift_start) return [];
68970
- const parseTimeToMinutes3 = (time2) => {
69221
+ const parseTimeToMinutes4 = (time2) => {
68971
69222
  const [h, m] = time2.split(":").map(Number);
68972
69223
  if (!Number.isFinite(h) || !Number.isFinite(m)) return 0;
68973
69224
  return h * 60 + m;
68974
69225
  };
68975
- const startTotal = parseTimeToMinutes3(workspace.shift_start);
68976
- const endTotalRaw = workspace.shift_end ? parseTimeToMinutes3(workspace.shift_end) : startTotal + 11 * 60;
69226
+ const startTotal = parseTimeToMinutes4(workspace.shift_start);
69227
+ const endTotalRaw = workspace.shift_end ? parseTimeToMinutes4(workspace.shift_end) : startTotal + 11 * 60;
68977
69228
  const endTotal = endTotalRaw <= startTotal ? endTotalRaw + 24 * 60 : endTotalRaw;
68978
69229
  const shiftDuration = Math.max(60, endTotal - startTotal);
68979
69230
  const totalHourSlots = Math.max(1, Math.ceil(shiftDuration / 60));
@@ -69023,6 +69274,18 @@ var WorkspaceDetailView = ({
69023
69274
  workspace.cycle_completion_clip_count
69024
69275
  ] })
69025
69276
  ] }), [workspace?.cycle_completion_clip_count]);
69277
+ const assemblyOverviewLoadingView = React141.useMemo(() => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 pb-4", "data-testid": "assembly-overview-loading-state", children: [
69278
+ /* @__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: [
69279
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-gray-200 rounded-full w-3/4 mx-auto" }),
69280
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-gray-100 rounded-full w-full" }),
69281
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 bg-gray-100 rounded-full w-5/6 mx-auto" })
69282
+ ] }) }) }),
69283
+ /* @__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: [
69284
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-24 bg-gray-200 rounded" }),
69285
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-10 w-20 bg-gray-100 rounded" }),
69286
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-28 bg-gray-100 rounded" })
69287
+ ] }) }, index)) })
69288
+ ] }), []);
69026
69289
  const shiftDurationMinutes = React141.useMemo(
69027
69290
  () => getShiftDurationMinutes(workspace?.shift_start, workspace?.shift_end),
69028
69291
  [workspace?.shift_start, workspace?.shift_end]
@@ -69077,7 +69340,7 @@ var WorkspaceDetailView = ({
69077
69340
  }, [isUptimeMode, uptimeSeries, shiftDurationMinutes, elapsedShiftMinutes, workspace?.idle_time]);
69078
69341
  const overviewTabLabel = isUptimeMode ? "Utilization" : "Efficiency";
69079
69342
  const idleClipFetchEnabled = Boolean(
69080
- workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && !shouldShowCycleTimeChart && !isUptimeMode
69343
+ workspaceId && idleClipDate && idleClipShiftId !== void 0 && activeTab === "overview" && idleTimeVlmEnabled && isOutputLayout
69081
69344
  );
69082
69345
  const {
69083
69346
  idleClips: idleTimeClips,
@@ -69148,7 +69411,7 @@ var WorkspaceDetailView = ({
69148
69411
  }
69149
69412
  }
69150
69413
  };
69151
- const getShiftIcon = (shiftType) => {
69414
+ const getShiftIcon2 = (shiftType) => {
69152
69415
  const shiftTypeLower = shiftType?.toLowerCase() || "";
69153
69416
  if (shiftTypeLower.includes("day") || shiftTypeLower.includes("morning")) {
69154
69417
  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 +69560,7 @@ var WorkspaceDetailView = ({
69297
69560
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: (() => {
69298
69561
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
69299
69562
  const shiftName = shift2?.shiftName || (selectedShift === 0 ? "Day Shift" : "Night Shift");
69300
- return getShiftIcon(shiftName);
69563
+ return getShiftIcon2(shiftName);
69301
69564
  })() }),
69302
69565
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: (() => {
69303
69566
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
@@ -69325,7 +69588,7 @@ var WorkspaceDetailView = ({
69325
69588
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: (() => {
69326
69589
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
69327
69590
  const shiftName = shift2?.shiftName || (selectedShift === 0 ? "Day Shift" : "Night Shift");
69328
- return getShiftIcon(shiftName);
69591
+ return getShiftIcon2(shiftName);
69329
69592
  })() }),
69330
69593
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: (() => {
69331
69594
  const shift2 = shiftConfig?.shifts?.find((s) => s.shiftId === selectedShift);
@@ -69338,7 +69601,7 @@ var WorkspaceDetailView = ({
69338
69601
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
69339
69602
  /* @__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
69603
  /* @__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) }),
69604
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon2(workspace.shift_type) }),
69342
69605
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: workspace.shift_type })
69343
69606
  ] }),
69344
69607
  !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 +69632,7 @@ var WorkspaceDetailView = ({
69369
69632
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-200" })
69370
69633
  ] }),
69371
69634
  /* @__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) }),
69635
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "opacity-70", children: getShiftIcon2(workspace.shift_type) }),
69373
69636
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-semibold uppercase tracking-wider", children: [
69374
69637
  workspace.shift_type.replace(/ Shift$/i, ""),
69375
69638
  " Shift"
@@ -69488,9 +69751,9 @@ var WorkspaceDetailView = ({
69488
69751
  ] })
69489
69752
  ] }),
69490
69753
  /* @__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: [
69754
+ 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
69755
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "block lg:hidden space-y-6 pb-6", children: [
69493
- !shouldShowCycleTimeChart && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69756
+ isOutputLayout && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69494
69757
  motion.div,
69495
69758
  {
69496
69759
  className: "bg-white rounded-lg shadow-sm p-6 h-[300px]",
@@ -69518,8 +69781,8 @@ var WorkspaceDetailView = ({
69518
69781
  animate: "animate",
69519
69782
  children: [
69520
69783
  /* @__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(
69784
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : isAssemblyCycleLayout ? "Cycle time trend" : "Hourly Output" }),
69785
+ canToggleChartIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
69523
69786
  "button",
69524
69787
  {
69525
69788
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69548,19 +69811,19 @@ var WorkspaceDetailView = ({
69548
69811
  timezone,
69549
69812
  elapsedMinutes: elapsedShiftMinutes
69550
69813
  }
69551
- ) : shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69814
+ ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69552
69815
  CycleTimeOverTimeChart,
69553
69816
  {
69554
69817
  data: maskedCycleTimeChartData,
69555
- idealCycleTime: workspace.ideal_cycle_time || 0,
69556
- shiftStart: workspace.shift_start || "",
69557
- shiftEnd: workspace.shift_end || "",
69818
+ idealCycleTime: authoritativeCycleMetrics?.ideal_cycle_time || 0,
69819
+ shiftStart: authoritativeCycleMetrics?.shift_start || "",
69820
+ shiftEnd: authoritativeCycleMetrics?.shift_end || "",
69558
69821
  xAxisMode: "hourly",
69559
69822
  datasetKey: cycleTimeDatasetKey,
69560
69823
  showIdleTime: showChartIdleTime,
69561
69824
  idleTimeData: hourlyIdleMinutes
69562
69825
  }
69563
- ) : /* @__PURE__ */ jsxRuntime.jsx(
69826
+ ) : null : /* @__PURE__ */ jsxRuntime.jsx(
69564
69827
  HourlyOutputChart2,
69565
69828
  {
69566
69829
  data: workspace.hourly_action_counts || [],
@@ -69580,7 +69843,7 @@ var WorkspaceDetailView = ({
69580
69843
  ]
69581
69844
  }
69582
69845
  ),
69583
- !shouldShowCycleTimeChart && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
69846
+ showIdleBreakdownChart && /* @__PURE__ */ jsxRuntime.jsxs(
69584
69847
  motion.div,
69585
69848
  {
69586
69849
  className: "bg-white rounded-lg shadow-sm p-4 h-[300px]",
@@ -69600,7 +69863,7 @@ var WorkspaceDetailView = ({
69600
69863
  ]
69601
69864
  }
69602
69865
  ),
69603
- isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData }) : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69866
+ isUptimeMode ? /* @__PURE__ */ jsxRuntime.jsx(UptimeMetricCards, { workspace, uptimePieData }) : isAssemblyCycleLayout ? /* @__PURE__ */ jsxRuntime.jsx(
69604
69867
  WorkspaceCycleTimeMetricCards,
69605
69868
  {
69606
69869
  workspace,
@@ -69620,7 +69883,7 @@ var WorkspaceDetailView = ({
69620
69883
  desktopTopSectionClass
69621
69884
  ),
69622
69885
  children: [
69623
- !shouldShowCycleTimeChart && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69886
+ isOutputLayout && !isUptimeMode && /* @__PURE__ */ jsxRuntime.jsxs(
69624
69887
  motion.div,
69625
69888
  {
69626
69889
  className: "bg-white rounded-lg shadow-sm p-4 lg:col-span-2 flex flex-col min-h-0",
@@ -69644,15 +69907,15 @@ var WorkspaceDetailView = ({
69644
69907
  {
69645
69908
  className: clsx(
69646
69909
  "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"
69910
+ isUptimeMode && showIdleBreakdownChart ? "lg:col-span-2" : isAssemblyCycleLayout || isUptimeMode ? "lg:col-span-10" : idleTimeVlmEnabled ? "lg:col-span-6" : "lg:col-span-8"
69648
69911
  ),
69649
69912
  variants: chartCardVariants,
69650
69913
  initial: "initial",
69651
69914
  animate: "animate",
69652
69915
  children: [
69653
69916
  /* @__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(
69917
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-bold text-gray-700", children: isUptimeMode ? "Machine Utilization" : isAssemblyCycleLayout ? "Cycle time trend" : "Hourly Output" }),
69918
+ canToggleChartIdleTime && /* @__PURE__ */ jsxRuntime.jsx(
69656
69919
  "button",
69657
69920
  {
69658
69921
  onClick: () => setShowChartIdleTime(!showChartIdleTime),
@@ -69677,19 +69940,19 @@ var WorkspaceDetailView = ({
69677
69940
  timezone,
69678
69941
  elapsedMinutes: elapsedShiftMinutes
69679
69942
  }
69680
- ) : shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69943
+ ) : isAssemblyCycleLayout ? shouldShowCycleTimeUnavailableState ? cycleTimeUnavailableView : shouldShowCycleTimeChart ? /* @__PURE__ */ jsxRuntime.jsx(
69681
69944
  CycleTimeOverTimeChart,
69682
69945
  {
69683
69946
  data: maskedCycleTimeChartData,
69684
- idealCycleTime: workspace.ideal_cycle_time || 0,
69685
- shiftStart: workspace.shift_start || "",
69686
- shiftEnd: workspace.shift_end || "",
69947
+ idealCycleTime: authoritativeCycleMetrics?.ideal_cycle_time || 0,
69948
+ shiftStart: authoritativeCycleMetrics?.shift_start || "",
69949
+ shiftEnd: authoritativeCycleMetrics?.shift_end || "",
69687
69950
  xAxisMode: "hourly",
69688
69951
  datasetKey: cycleTimeDatasetKey,
69689
69952
  showIdleTime: showChartIdleTime,
69690
69953
  idleTimeData: hourlyIdleMinutes
69691
69954
  }
69692
- ) : /* @__PURE__ */ jsxRuntime.jsx(
69955
+ ) : null : /* @__PURE__ */ jsxRuntime.jsx(
69693
69956
  HourlyOutputChart2,
69694
69957
  {
69695
69958
  data: workspace.hourly_action_counts || [],
@@ -69707,7 +69970,7 @@ var WorkspaceDetailView = ({
69707
69970
  ]
69708
69971
  }
69709
69972
  ),
69710
- !shouldShowCycleTimeChart && idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
69973
+ showIdleBreakdownChart && /* @__PURE__ */ jsxRuntime.jsxs(
69711
69974
  motion.div,
69712
69975
  {
69713
69976
  className: clsx(
@@ -69733,7 +69996,7 @@ var WorkspaceDetailView = ({
69733
69996
  ]
69734
69997
  }
69735
69998
  ),
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(
69999
+ 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
70000
  WorkspaceCycleTimeMetricCards,
69738
70001
  {
69739
70002
  workspace,
@@ -69744,7 +70007,7 @@ var WorkspaceDetailView = ({
69744
70007
  }
69745
70008
  ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: clsx("flex min-h-0", desktopBottomSectionClass), children: /* @__PURE__ */ jsxRuntime.jsx(WorkspaceMetricCards, { workspace, legend: efficiencyLegend, className: "flex-1" }) })
69746
70009
  ] })
69747
- ] }),
70010
+ ] }) }),
69748
70011
  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
70012
  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
70013
  /* @__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 +72639,7 @@ var normalizeLabel = (value) => {
72376
72639
  const trimmed = value.trim();
72377
72640
  return trimmed.length > 0 ? trimmed : void 0;
72378
72641
  };
72379
- var toFiniteNumber = (value) => {
72642
+ var toFiniteNumber2 = (value) => {
72380
72643
  if (typeof value === "number" && Number.isFinite(value)) return value;
72381
72644
  if (typeof value === "string" && value.trim().length > 0) {
72382
72645
  const parsed = Number(value);
@@ -72410,7 +72673,7 @@ var formatImprovementPieceGain = (pcsGain) => {
72410
72673
  var getPositiveImprovementGain = ({
72411
72674
  estimated_gain_pieces
72412
72675
  }) => {
72413
- const issueGain = toFiniteNumber(estimated_gain_pieces);
72676
+ const issueGain = toFiniteNumber2(estimated_gain_pieces);
72414
72677
  return {
72415
72678
  pcsGain: issueGain !== null && issueGain > 0 ? issueGain : null
72416
72679
  };
@@ -72418,7 +72681,7 @@ var getPositiveImprovementGain = ({
72418
72681
  var getImprovementPcsGainSortValue = (input) => {
72419
72682
  const { pcsGain } = getPositiveImprovementGain(input);
72420
72683
  if (pcsGain !== null) return pcsGain;
72421
- const raw = toFiniteNumber(input.estimated_gain_pieces);
72684
+ const raw = toFiniteNumber2(input.estimated_gain_pieces);
72422
72685
  return raw ?? null;
72423
72686
  };
72424
72687
  var getImprovementDisplayMetadata = ({
@@ -72438,7 +72701,7 @@ var getImprovementDisplayMetadata = ({
72438
72701
  metadataLabel: [workstationLabel, lineLabel, supervisorLabel].join(" \xB7 ")
72439
72702
  };
72440
72703
  };
72441
- var toFiniteNumber2 = (value) => {
72704
+ var toFiniteNumber3 = (value) => {
72442
72705
  if (typeof value === "number" && Number.isFinite(value)) return value;
72443
72706
  if (typeof value === "string" && value.trim().length > 0) {
72444
72707
  const parsed = Number(value);
@@ -72466,15 +72729,15 @@ var compareImprovementRecommendationPriority = (left, right) => {
72466
72729
  if (leftIndustrial !== rightIndustrial) {
72467
72730
  return rightIndustrial - leftIndustrial;
72468
72731
  }
72469
- const leftGain = toFiniteNumber2(left.estimated_gain_pieces);
72470
- const rightGain = toFiniteNumber2(right.estimated_gain_pieces);
72732
+ const leftGain = toFiniteNumber3(left.estimated_gain_pieces);
72733
+ const rightGain = toFiniteNumber3(right.estimated_gain_pieces);
72471
72734
  if (leftGain !== rightGain) {
72472
72735
  if (leftGain === null) return 1;
72473
72736
  if (rightGain === null) return -1;
72474
72737
  return rightGain - leftGain;
72475
72738
  }
72476
- const leftRatio = toFiniteNumber2(left.gain_to_target_ratio);
72477
- const rightRatio = toFiniteNumber2(right.gain_to_target_ratio);
72739
+ const leftRatio = toFiniteNumber3(left.gain_to_target_ratio);
72740
+ const rightRatio = toFiniteNumber3(right.gain_to_target_ratio);
72478
72741
  if (leftRatio !== rightRatio) {
72479
72742
  if (leftRatio === null) return 1;
72480
72743
  if (rightRatio === null) return -1;
@@ -75779,6 +76042,7 @@ var EMPTY_OVERVIEW_POOREST_LINES = {
75779
76042
  };
75780
76043
  var EMPTY_OVERVIEW_TREND = {
75781
76044
  shift_mode: "all",
76045
+ granularity: "day",
75782
76046
  points: []
75783
76047
  };
75784
76048
  var EMPTY_IDLE_BREAKDOWN = [];
@@ -75851,8 +76115,11 @@ var normalizePoorestLines = (value) => ({
75851
76115
  });
75852
76116
  var normalizeTrend = (value) => ({
75853
76117
  shift_mode: value?.shift_mode || "all",
76118
+ granularity: value?.granularity === "hour" ? "hour" : "day",
75854
76119
  points: (value?.points || []).map((point) => ({
75855
76120
  date: point?.date,
76121
+ label: point?.label,
76122
+ hour_index: normalizeNumber(point?.hour_index),
75856
76123
  avg_efficiency: normalizeNumber(point?.avg_efficiency)
75857
76124
  }))
75858
76125
  });
@@ -76194,17 +76461,25 @@ var formatSignedIdleDuration = (seconds) => {
76194
76461
  const sign = seconds > 0 ? "+" : "-";
76195
76462
  return `${sign}${formatIdleDuration(Math.abs(seconds))}`;
76196
76463
  };
76197
- var formatComparisonWindow = (dayCount, comparisonStrategy) => {
76464
+ var formatComparisonWindow = ({
76465
+ currentDayCount,
76466
+ previousDayCount,
76467
+ comparisonStrategy,
76468
+ shiftMode
76469
+ }) => {
76198
76470
  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"}`;
76471
+ if (comparisonStrategy === "matched_range" && currentDayCount === 1 && previousDayCount === 1) {
76472
+ return "previous day";
76473
+ }
76474
+ if (!previousDayCount || !Number.isFinite(previousDayCount)) return "previous range";
76475
+ return `previous ${previousDayCount} ${previousDayCount === 1 ? "day" : "days"}`;
76201
76476
  };
76202
76477
  var buildDeltaBadge = (delta, options) => {
76203
76478
  if (delta === null || delta === void 0 || !Number.isFinite(delta)) {
76204
76479
  return {
76205
76480
  icon: null,
76206
- className: "bg-slate-100 text-slate-500",
76207
- text: `No ${options.comparisonLabel} baseline`
76481
+ className: "bg-slate-100 text-slate-400",
76482
+ text: "\u2014"
76208
76483
  };
76209
76484
  }
76210
76485
  const direction = delta >= 0 ? "up" : "down";
@@ -76215,6 +76490,34 @@ var buildDeltaBadge = (delta, options) => {
76215
76490
  text: `${options.formatter(delta)} vs ${options.comparisonLabel}`
76216
76491
  };
76217
76492
  };
76493
+ var normalizeShiftLabel = (shiftName, shiftMode) => {
76494
+ if (shiftMode === "all") {
76495
+ return "All Shifts";
76496
+ }
76497
+ const trimmedName = shiftName?.trim();
76498
+ if (trimmedName) {
76499
+ const normalizedName = trimmedName.toLowerCase();
76500
+ if (normalizedName === "day") return "Day Shift";
76501
+ if (normalizedName === "night") return "Night Shift";
76502
+ return /shift/i.test(trimmedName) ? trimmedName : `${trimmedName} Shift`;
76503
+ }
76504
+ if (shiftMode === "night") return "Night Shift";
76505
+ return "Day Shift";
76506
+ };
76507
+ var getShiftIcon = (shiftName, shiftMode) => {
76508
+ const normalizedName = (shiftName || "").toLowerCase();
76509
+ const normalizedMode = shiftMode || "day";
76510
+ if (normalizedMode === "all") {
76511
+ 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" }) });
76512
+ }
76513
+ if (normalizedName.includes("day") || normalizedName.includes("morning") || normalizedMode === "day") {
76514
+ 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" }) });
76515
+ }
76516
+ if (normalizedName.includes("night") || normalizedName.includes("evening") || normalizedMode === "night") {
76517
+ 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" }) });
76518
+ }
76519
+ 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" }) });
76520
+ };
76218
76521
  var buildLineDeltaTone = (delta, comparisonLabel) => {
76219
76522
  if (delta === null || delta === void 0 || !Number.isFinite(delta)) {
76220
76523
  return {
@@ -76270,6 +76573,8 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76270
76573
  dateRange,
76271
76574
  displayDateRange,
76272
76575
  trendMode,
76576
+ isLiveScope,
76577
+ liveShiftName,
76273
76578
  lineOptions,
76274
76579
  supervisorOptions,
76275
76580
  selectedSupervisorId,
@@ -76283,6 +76588,15 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76283
76588
  }) => {
76284
76589
  bumpRenderCounter();
76285
76590
  const subtitleRange = displayDateRange || dateRange;
76591
+ const showLiveShiftMeta = isLiveScope && trendMode !== "all";
76592
+ const liveShiftLabel = React141__namespace.default.useMemo(
76593
+ () => normalizeShiftLabel(liveShiftName, trendMode),
76594
+ [liveShiftName, trendMode]
76595
+ );
76596
+ const liveShiftIcon = React141__namespace.default.useMemo(
76597
+ () => getShiftIcon(liveShiftName, trendMode),
76598
+ [liveShiftName, trendMode]
76599
+ );
76286
76600
  const [isFilterOpen, setIsFilterOpen] = React141__namespace.default.useState(false);
76287
76601
  const [isLinesDropdownOpen, setIsLinesDropdownOpen] = React141__namespace.default.useState(false);
76288
76602
  const filterRef = React141__namespace.default.useRef(null);
@@ -76399,9 +76713,25 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76399
76713
  className: "flex-shrink-0 -ml-1"
76400
76714
  }
76401
76715
  ) : /* @__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 })
76716
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col items-center justify-center min-w-0", children: [
76717
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-1.5 min-w-0 max-w-[240px]", children: [
76718
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-lg font-semibold text-gray-900 leading-tight text-center truncate", children: "Operations Overview" }),
76719
+ isLiveScope ? /* @__PURE__ */ jsxRuntime.jsx(
76720
+ "div",
76721
+ {
76722
+ "data-testid": "operations-overview-live-indicator",
76723
+ className: "h-2 w-2 rounded-full bg-emerald-500 animate-pulse ring-2 ring-emerald-500/20 flex-shrink-0"
76724
+ }
76725
+ ) : null
76726
+ ] }),
76727
+ /* @__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: [
76728
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-center", children: mobileSubtitle }),
76729
+ showLiveShiftMeta ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-300", children: "|" }) : null,
76730
+ showLiveShiftMeta ? /* @__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: [
76731
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75 shrink-0", children: liveShiftIcon }),
76732
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: liveShiftLabel })
76733
+ ] }) }) : null
76734
+ ] })
76405
76735
  ] }),
76406
76736
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 flex items-center gap-1.5", children: [
76407
76737
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -76420,22 +76750,40 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76420
76750
  {
76421
76751
  ref: mobileFilterButtonRef,
76422
76752
  onClick: handleFilterToggle,
76423
- className: `p-2 rounded-full transition-colors relative ${isFilterOpen || activeFilterCount > 0 ? "bg-blue-50" : "active:bg-gray-100"}`,
76753
+ 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
76754
  "aria-label": "Open filters",
76425
76755
  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
76756
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: "w-5 h-5" }),
76757
+ 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
76758
  ]
76429
76759
  }
76430
76760
  )
76431
76761
  ] })
76432
76762
  ] }) }),
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 })
76763
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center relative min-h-[64px]", children: [
76764
+ /* @__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: [
76765
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 min-w-0 max-w-full", children: [
76766
+ /* @__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" }),
76767
+ isLiveScope ? /* @__PURE__ */ jsxRuntime.jsx(
76768
+ "div",
76769
+ {
76770
+ "data-testid": "operations-overview-live-indicator",
76771
+ 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"
76772
+ }
76773
+ ) : null
76774
+ ] }),
76775
+ /* @__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: [
76776
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: desktopSubtitle }),
76777
+ showLiveShiftMeta ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
76778
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-300", children: "|" }),
76779
+ /* @__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: [
76780
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-75", children: liveShiftIcon }),
76781
+ liveShiftLabel
76782
+ ] }) })
76783
+ ] }) : null
76784
+ ] })
76437
76785
  ] }),
76438
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 flex items-center gap-3", children: [
76786
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 flex items-center gap-3 shrink-0", children: [
76439
76787
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntime.jsx(
76440
76788
  MonthlyRangeFilter_default,
76441
76789
  {
@@ -76452,12 +76800,12 @@ var OperationsOverviewHeader = React141__namespace.default.memo(({
76452
76800
  {
76453
76801
  ref: filterButtonRef,
76454
76802
  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"}`,
76803
+ 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
76804
  "aria-label": "Open filters",
76457
76805
  children: [
76458
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-[18px] h-[18px] ${activeFilterCount > 0 ? "text-blue-600" : "text-slate-500"}` }),
76806
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Filter, { className: `w-[18px] h-[18px] ${isFilterOpen || activeFilterCount > 0 ? "text-slate-500" : "text-slate-400"}` }),
76459
76807
  "Filters",
76460
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-4 h-4 ml-0.5 transition-transform duration-200 ${isFilterOpen ? "rotate-180" : ""}` })
76808
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-4 h-4 ml-0.5 text-slate-400 transition-transform duration-200 ${isFilterOpen ? "rotate-180" : ""}` })
76461
76809
  ]
76462
76810
  }
76463
76811
  )
@@ -76585,11 +76933,18 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76585
76933
  const snapshot = useOperationsOverviewSnapshot(store);
76586
76934
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
76587
76935
  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]);
76936
+ return formatComparisonWindow({
76937
+ currentDayCount: scope.current_range?.day_count ?? null,
76938
+ previousDayCount: scope.previous_range?.day_count ?? null,
76939
+ comparisonStrategy: scope.comparison_strategy,
76940
+ shiftMode: scope.shift_mode
76941
+ });
76942
+ }, [
76943
+ scope.comparison_strategy,
76944
+ scope.current_range?.day_count,
76945
+ scope.previous_range?.day_count,
76946
+ scope.shift_mode
76947
+ ]);
76593
76948
  const [isIdleContributorsOpen, setIsIdleContributorsOpen] = React141__namespace.default.useState(false);
76594
76949
  const [isIdleContributorsPinned, setIsIdleContributorsPinned] = React141__namespace.default.useState(false);
76595
76950
  const idleContributorsRef = React141__namespace.default.useRef(null);
@@ -76679,10 +77034,17 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76679
77034
  roundOne(snapshot.data.summary.plant_efficiency.current),
76680
77035
  "%"
76681
77036
  ] }),
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
- ] })
77037
+ /* @__PURE__ */ jsxRuntime.jsxs(
77038
+ "div",
77039
+ {
77040
+ "data-testid": "operations-overview-efficiency-delta",
77041
+ className: `flex items-center gap-1 px-2.5 py-1 rounded-full ${plantEfficiencyBadge.className}`,
77042
+ children: [
77043
+ 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,
77044
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium", children: plantEfficiencyBadge.text })
77045
+ ]
77046
+ }
77047
+ )
76686
77048
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-sm text-slate-400", children: "No efficiency data available" })
76687
77049
  ] }),
76688
77050
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -76728,10 +77090,17 @@ var OverviewSummaryCards = React141__namespace.default.memo(({ store }) => {
76728
77090
  /* @__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
77091
  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
77092
  /* @__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
- ] })
77093
+ /* @__PURE__ */ jsxRuntime.jsxs(
77094
+ "div",
77095
+ {
77096
+ "data-testid": "operations-overview-idle-delta",
77097
+ className: `flex items-center gap-1 px-2.5 py-1 rounded-full ${idleBadge.className}`,
77098
+ children: [
77099
+ 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,
77100
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-sm font-medium", children: idleBadge.text })
77101
+ ]
77102
+ }
77103
+ )
76735
77104
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-sm text-slate-400", children: "No idle time data available" })
76736
77105
  ]
76737
77106
  }
@@ -76794,11 +77163,18 @@ var PoorestPerformersCard = React141__namespace.default.memo(({
76794
77163
  }
76795
77164
  }, [availableLineModes?.has_output, availableLineModes?.has_uptime, poorestLineMode]);
76796
77165
  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]);
77166
+ return formatComparisonWindow({
77167
+ currentDayCount: scope.current_range?.day_count ?? null,
77168
+ previousDayCount: scope.previous_range?.day_count ?? null,
77169
+ comparisonStrategy: scope.comparison_strategy,
77170
+ shiftMode: scope.shift_mode
77171
+ });
77172
+ }, [
77173
+ scope.comparison_strategy,
77174
+ scope.current_range?.day_count,
77175
+ scope.previous_range?.day_count,
77176
+ scope.shift_mode
77177
+ ]);
76802
77178
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
76803
77179
  const mergedPoorestLines = React141__namespace.default.useMemo(() => {
76804
77180
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
@@ -76955,7 +77331,8 @@ IdleBreakdownCard.displayName = "IdleBreakdownCard";
76955
77331
  var EfficiencyTrendCard = React141__namespace.default.memo(({
76956
77332
  store,
76957
77333
  dateRange,
76958
- appTimezone
77334
+ appTimezone,
77335
+ hourlyLabelStartTime
76959
77336
  }) => {
76960
77337
  bumpRenderCounter();
76961
77338
  const trend = useOperationsOverviewTrend(store);
@@ -76965,7 +77342,41 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
76965
77342
  );
76966
77343
  const isCurrentWeekToDateRange = dateRange.startKey === currentWeekRange.startKey && dateRange.endKey === currentWeekRange.endKey;
76967
77344
  const showInitialSkeleton = trend.loading && trend.lastUpdated === null;
77345
+ const isHourlyTrend = trend.data.granularity === "hour";
76968
77346
  const trendData = React141__namespace.default.useMemo(() => {
77347
+ if (isHourlyTrend) {
77348
+ return (trend.data.points || []).map((point, index) => ({
77349
+ name: (() => {
77350
+ const rawLabel = point.label?.trim() || "";
77351
+ if (rawLabel) {
77352
+ return rawLabel;
77353
+ }
77354
+ if (!hourlyLabelStartTime) {
77355
+ return "";
77356
+ }
77357
+ const hourIndex = typeof point.hour_index === "number" ? point.hour_index : index;
77358
+ const [hoursPart, minutesPart] = hourlyLabelStartTime.split(":");
77359
+ const startHours = Number(hoursPart);
77360
+ const startMinutes = Number(minutesPart);
77361
+ if (!Number.isFinite(startHours) || !Number.isFinite(startMinutes)) {
77362
+ return "";
77363
+ }
77364
+ const totalMinutes = startHours * 60 + startMinutes + hourIndex * 60;
77365
+ const hour24 = Math.floor(totalMinutes / 60) % 24;
77366
+ const minutes = totalMinutes % 60;
77367
+ const suffix = hour24 < 12 ? "AM" : "PM";
77368
+ const hour12 = hour24 % 12 || 12;
77369
+ if (minutes === 0) {
77370
+ return `${hour12} ${suffix}`;
77371
+ }
77372
+ return `${hour12}:${minutes.toString().padStart(2, "0")} ${suffix}`;
77373
+ })(),
77374
+ efficiency: (() => {
77375
+ const value = toNumber3(point.avg_efficiency);
77376
+ return value === null ? void 0 : value;
77377
+ })()
77378
+ }));
77379
+ }
76969
77380
  const pointsByDate = new Map(
76970
77381
  (trend.data.points || []).flatMap((point) => {
76971
77382
  if (!point.date) return [];
@@ -77003,12 +77414,19 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
77003
77414
  })()
77004
77415
  };
77005
77416
  });
77006
- }, [currentWeekRange.startKey, isCurrentWeekToDateRange, trend.data.points]);
77417
+ }, [currentWeekRange.startKey, hourlyLabelStartTime, isCurrentWeekToDateRange, isHourlyTrend, trend.data.points]);
77007
77418
  const trendTooltipLabelFormatter = React141__namespace.default.useCallback((label, payload) => {
77419
+ if (isHourlyTrend) return label;
77008
77420
  const dayOfWeek = payload?.[0]?.payload?.dayOfWeek;
77009
77421
  if (!dayOfWeek || typeof label !== "string") return label;
77010
77422
  return `${label} (${dayOfWeek})`;
77011
- }, []);
77423
+ }, [isHourlyTrend]);
77424
+ const trendXAxisTickFormatter = React141__namespace.default.useCallback((value, index) => {
77425
+ if (!isHourlyTrend) {
77426
+ return typeof value === "string" ? value : String(value ?? "");
77427
+ }
77428
+ return index % 2 === 0 ? typeof value === "string" ? value : String(value ?? "") : "";
77429
+ }, [isHourlyTrend]);
77012
77430
  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
77431
  /* @__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
77432
  /* @__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(
@@ -77017,6 +77435,8 @@ var EfficiencyTrendCard = React141__namespace.default.memo(({
77017
77435
  data: trendData,
77018
77436
  lines: efficiencyLineConfig,
77019
77437
  xAxisDataKey: "name",
77438
+ xAxisInterval: isHourlyTrend ? 0 : void 0,
77439
+ xAxisTickFormatter: trendXAxisTickFormatter,
77020
77440
  yAxisUnit: "%",
77021
77441
  yAxisDomain: [0, 100],
77022
77442
  showLegend: false,
@@ -77162,7 +77582,8 @@ var useOperationsOverviewRefresh = ({
77162
77582
  endKey,
77163
77583
  trendMode,
77164
77584
  comparisonStrategy,
77165
- isLiveScope
77585
+ isLiveScope,
77586
+ enabled = true
77166
77587
  }) => {
77167
77588
  const lineIdsKey = React141__namespace.default.useMemo(() => lineIds.join(","), [lineIds]);
77168
77589
  const scopeSignature = React141__namespace.default.useMemo(
@@ -77208,7 +77629,7 @@ var useOperationsOverviewRefresh = ({
77208
77629
  }, []);
77209
77630
  const runRefresh = React141__namespace.default.useCallback(
77210
77631
  async (section, begin, onSuccess, onError, request, reason) => {
77211
- if (!supabase || !companyId || lineIds.length === 0) return;
77632
+ if (!enabled || !supabase || !companyId || lineIds.length === 0) return;
77212
77633
  const requestId = requestIdsRef.current[section] + 1;
77213
77634
  requestIdsRef.current[section] = requestId;
77214
77635
  controllersRef.current[section]?.abort();
@@ -77228,7 +77649,7 @@ var useOperationsOverviewRefresh = ({
77228
77649
  onError(error instanceof Error ? error.message : `Failed to refresh ${section}`);
77229
77650
  }
77230
77651
  },
77231
- [companyId, lineIds.length, supabase]
77652
+ [companyId, enabled, lineIds.length, supabase]
77232
77653
  );
77233
77654
  const refreshSnapshot = React141__namespace.default.useCallback(
77234
77655
  async (reason) => {
@@ -77398,6 +77819,12 @@ var useOperationsOverviewRefresh = ({
77398
77819
  });
77399
77820
  }, [refreshAll, startPolling, stopPolling]);
77400
77821
  React141__namespace.default.useEffect(() => {
77822
+ if (!enabled) {
77823
+ stopPolling("disabled");
77824
+ abortAll();
77825
+ store.reset();
77826
+ return;
77827
+ }
77401
77828
  if (!supabase || !companyId || lineIds.length === 0) {
77402
77829
  stopPolling("scope_invalid");
77403
77830
  abortAll();
@@ -77405,9 +77832,9 @@ var useOperationsOverviewRefresh = ({
77405
77832
  return;
77406
77833
  }
77407
77834
  void refreshAll("scope_change");
77408
- }, [abortAll, companyId, lineIds.length, refreshAll, scopeSignature, stopPolling, store, supabase]);
77835
+ }, [abortAll, companyId, enabled, lineIds.length, refreshAll, scopeSignature, stopPolling, store, supabase]);
77409
77836
  React141__namespace.default.useEffect(() => {
77410
- if (!isLiveScope || !supabase || !companyId || lineIds.length === 0) {
77837
+ if (!enabled || !isLiveScope || !supabase || !companyId || lineIds.length === 0) {
77411
77838
  isPageActiveRef.current = false;
77412
77839
  stopPolling("live_scope_disabled");
77413
77840
  return;
@@ -77463,11 +77890,119 @@ var useOperationsOverviewRefresh = ({
77463
77890
  window.removeEventListener("pageshow", handlePageShow);
77464
77891
  window.removeEventListener("pagehide", handlePageHide);
77465
77892
  };
77466
- }, [companyId, getIsPageActive, isLiveScope, lineIds.length, refreshFromResume, startPolling, stopPolling, supabase]);
77893
+ }, [companyId, enabled, getIsPageActive, isLiveScope, lineIds.length, refreshFromResume, startPolling, stopPolling, supabase]);
77894
+ };
77895
+ var parseTimeToMinutes3 = (value) => {
77896
+ if (!value) return null;
77897
+ const parts = value.split(":");
77898
+ if (parts.length < 2) return null;
77899
+ const hours = Number(parts[0]);
77900
+ const minutes = Number(parts[1]);
77901
+ if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return null;
77902
+ if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null;
77903
+ return hours * 60 + minutes;
77904
+ };
77905
+ var normalizeShiftId = (value) => {
77906
+ if (typeof value === "number" && Number.isFinite(value)) {
77907
+ return value;
77908
+ }
77909
+ if (typeof value === "string" && value.trim().length > 0) {
77910
+ const parsed = Number(value);
77911
+ return Number.isFinite(parsed) ? parsed : null;
77912
+ }
77913
+ return null;
77914
+ };
77915
+ var classifyShiftBucket = ({
77916
+ shiftName,
77917
+ shiftId,
77918
+ startTime,
77919
+ endTime
77920
+ }) => {
77921
+ const normalizedName = (shiftName || "").trim().toLowerCase();
77922
+ if (normalizedName) {
77923
+ if (["night", "graveyard", "evening"].some((keyword) => normalizedName.includes(keyword))) {
77924
+ return "night";
77925
+ }
77926
+ if (["day", "morning"].some((keyword) => normalizedName.includes(keyword))) {
77927
+ return "day";
77928
+ }
77929
+ }
77930
+ const startMinutes = parseTimeToMinutes3(startTime);
77931
+ const endMinutes = parseTimeToMinutes3(endTime);
77932
+ if (startMinutes !== null) {
77933
+ if (startMinutes >= 4 * 60 && startMinutes < 18 * 60) return "day";
77934
+ return "night";
77935
+ }
77936
+ if (endMinutes !== null) {
77937
+ if (endMinutes >= 6 * 60 && endMinutes <= 21 * 60) return "day";
77938
+ return "night";
77939
+ }
77940
+ const normalizedShiftId = normalizeShiftId(shiftId);
77941
+ if (normalizedShiftId === 0) return "day";
77942
+ if (normalizedShiftId === 1) return "night";
77943
+ return null;
77944
+ };
77945
+ var getShiftWindowsForConfig = (shiftConfig, timezone) => {
77946
+ if (shiftConfig?.shifts && shiftConfig.shifts.length > 0) {
77947
+ return shiftConfig.shifts.map((shift) => ({
77948
+ shiftId: shift.shiftId,
77949
+ shiftName: shift.shiftName,
77950
+ startTime: shift.startTime,
77951
+ endTime: shift.endTime
77952
+ }));
77953
+ }
77954
+ const windows = [];
77955
+ if (shiftConfig?.dayShift) {
77956
+ windows.push({
77957
+ shiftId: shiftConfig.dayShift.id ?? 0,
77958
+ shiftName: shiftConfig.dayShift.name || "Day Shift",
77959
+ startTime: shiftConfig.dayShift.startTime || "06:00",
77960
+ endTime: shiftConfig.dayShift.endTime || "18:00"
77961
+ });
77962
+ }
77963
+ if (shiftConfig?.nightShift) {
77964
+ windows.push({
77965
+ shiftId: shiftConfig.nightShift.id ?? 1,
77966
+ shiftName: shiftConfig.nightShift.name || "Night Shift",
77967
+ startTime: shiftConfig.nightShift.startTime || "18:00",
77968
+ endTime: shiftConfig.nightShift.endTime || "06:00"
77969
+ });
77970
+ }
77971
+ if (windows.length > 0) {
77972
+ return windows;
77973
+ }
77974
+ return [
77975
+ {
77976
+ shiftId: 0,
77977
+ shiftName: "Day Shift",
77978
+ startTime: "06:00",
77979
+ endTime: "18:00"
77980
+ },
77981
+ {
77982
+ shiftId: 1,
77983
+ shiftName: "Night Shift",
77984
+ startTime: "18:00",
77985
+ endTime: "06:00"
77986
+ }
77987
+ ];
77988
+ };
77989
+ var normalizeShiftWindowMinutes = (startTime, endTime) => {
77990
+ const startMinutes = parseTimeToMinutes3(startTime);
77991
+ const endMinutesRaw = parseTimeToMinutes3(endTime);
77992
+ if (startMinutes === null || endMinutesRaw === null) {
77993
+ return null;
77994
+ }
77995
+ let endMinutes = endMinutesRaw;
77996
+ if (endMinutes <= startMinutes) {
77997
+ endMinutes += 24 * 60;
77998
+ }
77999
+ return { startMinutes, endMinutes };
77467
78000
  };
77468
78001
  var PlantHeadView = () => {
77469
78002
  const supabase = useSupabase();
77470
78003
  const entityConfig = useEntityConfig();
78004
+ const factoryViewId = entityConfig.factoryViewId || "factory";
78005
+ const staticShiftConfig = useShiftConfig();
77471
78006
  const appTimezone = useAppTimezone() || "UTC";
77472
78007
  const { navigate } = useNavigation();
77473
78008
  const { accessibleLineIds } = useUserLineAccess();
@@ -77475,11 +78010,22 @@ var PlantHeadView = () => {
77475
78010
  useHideMobileHeader(!!mobileMenuContext);
77476
78011
  const storeRef = React141__namespace.default.useRef(createOperationsOverviewStore());
77477
78012
  const store = storeRef.current;
77478
- const [dateRange, setDateRange] = React141__namespace.default.useState(() => getCurrentWeekToDateRange(appTimezone));
77479
- const [usesThisWeekComparison, setUsesThisWeekComparison] = React141__namespace.default.useState(true);
78013
+ const fallbackOperationalDate = React141__namespace.default.useMemo(
78014
+ () => getOperationalDate(appTimezone),
78015
+ [appTimezone]
78016
+ );
78017
+ const [dateRange, setDateRange] = React141__namespace.default.useState(() => ({
78018
+ startKey: fallbackOperationalDate,
78019
+ endKey: fallbackOperationalDate
78020
+ }));
78021
+ const [usesThisWeekComparison, setUsesThisWeekComparison] = React141__namespace.default.useState(false);
77480
78022
  const [trendMode, setTrendMode] = React141__namespace.default.useState("all");
77481
78023
  const [selectedSupervisorId, setSelectedSupervisorId] = React141__namespace.default.useState("all");
77482
78024
  const [selectedLineIds, setSelectedLineIds] = React141__namespace.default.useState([]);
78025
+ const [isInitialScopeReady, setIsInitialScopeReady] = React141__namespace.default.useState(false);
78026
+ const [shiftResolutionTick, setShiftResolutionTick] = React141__namespace.default.useState(0);
78027
+ const hasAutoInitializedScopeRef = React141__namespace.default.useRef(false);
78028
+ const hasUserAdjustedScopeRef = React141__namespace.default.useRef(false);
77483
78029
  React141__namespace.default.useEffect(() => {
77484
78030
  trackCorePageView("Operations Overview", {
77485
78031
  dashboard_surface: "operations_overview"
@@ -77501,8 +78047,10 @@ var PlantHeadView = () => {
77501
78047
  return dateRange;
77502
78048
  }, [currentWeekDisplayRange, dateRange, isCurrentWeekToDateRange, usesThisWeekComparison]);
77503
78049
  const normalizedLineIds = React141__namespace.default.useMemo(
77504
- () => Array.from(new Set((accessibleLineIds || []).filter(Boolean))).sort(),
77505
- [accessibleLineIds]
78050
+ () => Array.from(new Set(
78051
+ (accessibleLineIds || []).filter(Boolean).filter((lineId) => lineId !== factoryViewId)
78052
+ )).sort(),
78053
+ [accessibleLineIds, factoryViewId]
77506
78054
  );
77507
78055
  const lineIdsKey = React141__namespace.default.useMemo(
77508
78056
  () => normalizedLineIds.join(","),
@@ -77576,14 +78124,198 @@ var PlantHeadView = () => {
77576
78124
  () => selectedLineIds.length > 0 ? selectedLineIds : normalizedLineIds,
77577
78125
  [normalizedLineIds, selectedLineIds]
77578
78126
  );
78127
+ const {
78128
+ shiftConfigMap,
78129
+ isLoading: isShiftConfigLoading
78130
+ } = useMultiLineShiftConfigs(scopedLineIds, staticShiftConfig);
78131
+ React141__namespace.default.useEffect(() => {
78132
+ if (scopedLineIds.length === 0 || isShiftConfigLoading) {
78133
+ return;
78134
+ }
78135
+ const intervalId = window.setInterval(() => {
78136
+ setShiftResolutionTick((previous) => previous + 1);
78137
+ }, 6e4);
78138
+ return () => {
78139
+ clearInterval(intervalId);
78140
+ };
78141
+ }, [isShiftConfigLoading, scopedLineIds.length]);
78142
+ const shiftResolutionNow = React141__namespace.default.useMemo(
78143
+ () => /* @__PURE__ */ new Date(),
78144
+ [shiftResolutionTick]
78145
+ );
78146
+ const earliestDayShiftStartTime = React141__namespace.default.useMemo(() => {
78147
+ const candidateStarts = [];
78148
+ scopedLineIds.forEach((lineId) => {
78149
+ const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
78150
+ getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
78151
+ const bucket = classifyShiftBucket({
78152
+ shiftName: shift.shiftName,
78153
+ shiftId: shift.shiftId,
78154
+ startTime: shift.startTime,
78155
+ endTime: shift.endTime
78156
+ });
78157
+ const startMinutes = parseTimeToMinutes3(shift.startTime);
78158
+ if (bucket === "day" && startMinutes !== null) {
78159
+ candidateStarts.push(startMinutes);
78160
+ }
78161
+ });
78162
+ });
78163
+ if (candidateStarts.length === 0) {
78164
+ scopedLineIds.forEach((lineId) => {
78165
+ const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
78166
+ getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
78167
+ const startMinutes = parseTimeToMinutes3(shift.startTime);
78168
+ if (startMinutes !== null) {
78169
+ candidateStarts.push(startMinutes);
78170
+ }
78171
+ });
78172
+ });
78173
+ }
78174
+ if (candidateStarts.length === 0) {
78175
+ return "06:00";
78176
+ }
78177
+ const earliestMinutes = Math.min(...candidateStarts);
78178
+ const hours = Math.floor(earliestMinutes / 60);
78179
+ const minutes = earliestMinutes % 60;
78180
+ return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
78181
+ }, [appTimezone, scopedLineIds, shiftConfigMap, staticShiftConfig]);
78182
+ const resolvedOperationalToday = React141__namespace.default.useMemo(
78183
+ () => getOperationalDate(appTimezone, shiftResolutionNow, earliestDayShiftStartTime),
78184
+ [appTimezone, earliestDayShiftStartTime, shiftResolutionNow]
78185
+ );
78186
+ const activeLineShiftStates = React141__namespace.default.useMemo(() => {
78187
+ return scopedLineIds.flatMap((lineId) => {
78188
+ const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
78189
+ const activeShift = getActiveShift(appTimezone, shiftConfig, shiftResolutionNow);
78190
+ if (!activeShift) {
78191
+ return [];
78192
+ }
78193
+ const trendBucket = classifyShiftBucket({
78194
+ shiftName: activeShift.shiftName,
78195
+ shiftId: activeShift.shiftId,
78196
+ startTime: activeShift.startTime,
78197
+ endTime: activeShift.endTime
78198
+ });
78199
+ if (!trendBucket || trendBucket === "all") {
78200
+ return [];
78201
+ }
78202
+ return [{
78203
+ lineId,
78204
+ trendMode: trendBucket,
78205
+ shiftId: activeShift.shiftId,
78206
+ shiftName: activeShift.shiftName || null,
78207
+ startTime: activeShift.startTime || null,
78208
+ endTime: activeShift.endTime || null,
78209
+ date: activeShift.date
78210
+ }];
78211
+ });
78212
+ }, [appTimezone, scopedLineIds, shiftConfigMap, shiftResolutionNow, staticShiftConfig]);
78213
+ const hasActiveDayShiftLine = React141__namespace.default.useMemo(
78214
+ () => activeLineShiftStates.some((shift) => shift.trendMode === "day" && shift.date === resolvedOperationalToday),
78215
+ [activeLineShiftStates, resolvedOperationalToday]
78216
+ );
78217
+ const hasActiveNightShiftLine = React141__namespace.default.useMemo(
78218
+ () => activeLineShiftStates.some((shift) => shift.trendMode === "night" && shift.date === resolvedOperationalToday),
78219
+ [activeLineShiftStates, resolvedOperationalToday]
78220
+ );
78221
+ const resolvedTrendMode = isInitialScopeReady ? trendMode : "all";
78222
+ const hourlyWindowStartTime = React141__namespace.default.useMemo(() => {
78223
+ if (scopedLineIds.length === 0) {
78224
+ return null;
78225
+ }
78226
+ const startCandidates = [];
78227
+ const endCandidates = [];
78228
+ scopedLineIds.forEach((lineId) => {
78229
+ const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
78230
+ getShiftWindowsForConfig(shiftConfig).forEach((shift) => {
78231
+ const bucket = classifyShiftBucket({
78232
+ shiftName: shift.shiftName,
78233
+ shiftId: shift.shiftId,
78234
+ startTime: shift.startTime,
78235
+ endTime: shift.endTime
78236
+ });
78237
+ if (resolvedTrendMode !== "all" && bucket !== resolvedTrendMode) {
78238
+ return;
78239
+ }
78240
+ const normalizedWindow = normalizeShiftWindowMinutes(shift.startTime, shift.endTime);
78241
+ if (!normalizedWindow) {
78242
+ return;
78243
+ }
78244
+ startCandidates.push(normalizedWindow.startMinutes);
78245
+ endCandidates.push(normalizedWindow.endMinutes);
78246
+ });
78247
+ });
78248
+ if (resolvedTrendMode === "all") {
78249
+ const dayStartCandidates = startCandidates.length > 0 ? scopedLineIds.flatMap((lineId) => {
78250
+ const shiftConfig = shiftConfigMap.get(lineId) || staticShiftConfig;
78251
+ return getShiftWindowsForConfig(shiftConfig).map((shift) => {
78252
+ const bucket = classifyShiftBucket({
78253
+ shiftName: shift.shiftName,
78254
+ shiftId: shift.shiftId,
78255
+ startTime: shift.startTime,
78256
+ endTime: shift.endTime
78257
+ });
78258
+ return bucket === "day" ? parseTimeToMinutes3(shift.startTime) : null;
78259
+ }).filter((value) => value !== null);
78260
+ }) : [];
78261
+ if (dayStartCandidates.length > 0) {
78262
+ startCandidates.splice(0, startCandidates.length, ...dayStartCandidates);
78263
+ }
78264
+ }
78265
+ if (startCandidates.length === 0 || endCandidates.length === 0) {
78266
+ return null;
78267
+ }
78268
+ const earliestMinutes = Math.min(...startCandidates);
78269
+ const hours = Math.floor(earliestMinutes / 60);
78270
+ const minutes = earliestMinutes % 60;
78271
+ return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
78272
+ }, [appTimezone, resolvedTrendMode, scopedLineIds, shiftConfigMap, staticShiftConfig]);
78273
+ const isShiftScopeResolved = React141__namespace.default.useMemo(
78274
+ () => !isShiftConfigLoading,
78275
+ [isShiftConfigLoading]
78276
+ );
77579
78277
  const initializedTimezoneRef = React141__namespace.default.useRef(appTimezone);
77580
78278
  React141__namespace.default.useEffect(() => {
77581
78279
  if (initializedTimezoneRef.current === appTimezone) return;
77582
- setDateRange(getCurrentWeekToDateRange(appTimezone));
77583
- setUsesThisWeekComparison(true);
78280
+ hasAutoInitializedScopeRef.current = false;
78281
+ hasUserAdjustedScopeRef.current = false;
78282
+ setDateRange({
78283
+ startKey: fallbackOperationalDate,
78284
+ endKey: fallbackOperationalDate
78285
+ });
78286
+ setTrendMode("all");
78287
+ setUsesThisWeekComparison(false);
78288
+ setIsInitialScopeReady(false);
77584
78289
  initializedTimezoneRef.current = appTimezone;
77585
- }, [appTimezone]);
78290
+ }, [appTimezone, fallbackOperationalDate]);
78291
+ React141__namespace.default.useEffect(() => {
78292
+ if (hasAutoInitializedScopeRef.current || hasUserAdjustedScopeRef.current) {
78293
+ return;
78294
+ }
78295
+ if (scopedLineIds.length === 0) {
78296
+ return;
78297
+ }
78298
+ if (!isShiftScopeResolved) {
78299
+ return;
78300
+ }
78301
+ setDateRange((previous) => {
78302
+ const nextStartKey = resolvedOperationalToday || fallbackOperationalDate;
78303
+ if (previous.startKey === nextStartKey && previous.endKey === nextStartKey) {
78304
+ return previous;
78305
+ }
78306
+ return {
78307
+ startKey: nextStartKey,
78308
+ endKey: nextStartKey
78309
+ };
78310
+ });
78311
+ setTrendMode("all");
78312
+ setUsesThisWeekComparison(false);
78313
+ hasAutoInitializedScopeRef.current = true;
78314
+ setIsInitialScopeReady(true);
78315
+ }, [fallbackOperationalDate, isShiftScopeResolved, resolvedOperationalToday, scopedLineIds.length]);
77586
78316
  const handleDateRangeChange = React141__namespace.default.useCallback((range, meta) => {
78317
+ hasUserAdjustedScopeRef.current = true;
78318
+ setIsInitialScopeReady(true);
77587
78319
  trackCoreEvent("Operations Overview Date Range Changed", {
77588
78320
  start_date: range.startKey,
77589
78321
  end_date: range.endKey
@@ -77600,6 +78332,8 @@ var PlantHeadView = () => {
77600
78332
  });
77601
78333
  }, []);
77602
78334
  const handleTrendModeChange = React141__namespace.default.useCallback((mode) => {
78335
+ hasUserAdjustedScopeRef.current = true;
78336
+ setIsInitialScopeReady(true);
77603
78337
  setTrendMode(mode);
77604
78338
  }, []);
77605
78339
  const handleSelectedLineIdsChange = React141__namespace.default.useCallback((lineIds) => {
@@ -77626,15 +78360,6 @@ var PlantHeadView = () => {
77626
78360
  params.set("rangeEnd", dateRange.endKey);
77627
78361
  return `/kpis/${lineId}?${params.toString()}`;
77628
78362
  }, [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
78363
  const handleViewAllPoorestPerformers = React141__namespace.default.useCallback(() => {
77639
78364
  trackCoreEvent("Operations Overview View All Clicked", { section: "poorest_performers" });
77640
78365
  navigate("/kpis?tab=leaderboard");
@@ -77660,16 +78385,65 @@ var PlantHeadView = () => {
77660
78385
  }
77661
78386
  return void 0;
77662
78387
  }, [isCurrentWeekToDateRange, usesThisWeekComparison]);
78388
+ const effectiveDateRange = React141__namespace.default.useMemo(() => {
78389
+ if (isInitialScopeReady) {
78390
+ return dateRange;
78391
+ }
78392
+ const nextStartKey = resolvedOperationalToday || fallbackOperationalDate;
78393
+ return {
78394
+ startKey: nextStartKey,
78395
+ endKey: nextStartKey
78396
+ };
78397
+ }, [dateRange, fallbackOperationalDate, isInitialScopeReady, resolvedOperationalToday]);
78398
+ const effectiveTrendMode = React141__namespace.default.useMemo(
78399
+ () => resolvedTrendMode,
78400
+ [resolvedTrendMode]
78401
+ );
78402
+ const hourlyLabelStartTime = React141__namespace.default.useMemo(() => {
78403
+ if (scopedLineIds.length === 0) {
78404
+ return null;
78405
+ }
78406
+ return hourlyWindowStartTime;
78407
+ }, [hourlyWindowStartTime, scopedLineIds.length]);
78408
+ const isSingleDayScope = React141__namespace.default.useMemo(
78409
+ () => effectiveDateRange.startKey === effectiveDateRange.endKey,
78410
+ [effectiveDateRange.endKey, effectiveDateRange.startKey]
78411
+ );
78412
+ const isLiveScope = React141__namespace.default.useMemo(
78413
+ () => isSingleDayScope && effectiveDateRange.startKey === resolvedOperationalToday && (effectiveTrendMode === "all" || effectiveTrendMode === "day" && hasActiveDayShiftLine || effectiveTrendMode === "night" && hasActiveNightShiftLine),
78414
+ [
78415
+ effectiveDateRange.startKey,
78416
+ effectiveTrendMode,
78417
+ hasActiveDayShiftLine,
78418
+ hasActiveNightShiftLine,
78419
+ isSingleDayScope,
78420
+ resolvedOperationalToday
78421
+ ]
78422
+ );
78423
+ const handleOpenLineDetails = React141__namespace.default.useCallback((lineId, lineName) => {
78424
+ trackCoreEvent("Operations Overview Line Clicked", {
78425
+ line_id: lineId,
78426
+ line_name: lineName,
78427
+ range_start: dateRange.startKey,
78428
+ range_end: dateRange.endKey
78429
+ });
78430
+ if (isLiveScope) {
78431
+ navigate(`/kpis/${lineId}?returnTo=${encodeURIComponent("/")}`);
78432
+ return;
78433
+ }
78434
+ navigate(buildLineMonthlyHistoryUrl(lineId));
78435
+ }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, isLiveScope, navigate]);
77663
78436
  useOperationsOverviewRefresh({
77664
78437
  store,
77665
78438
  supabase,
77666
78439
  companyId: entityConfig.companyId,
77667
78440
  lineIds: scopedLineIds,
77668
- startKey: dateRange.startKey,
77669
- endKey: dateRange.endKey,
77670
- trendMode,
78441
+ startKey: effectiveDateRange.startKey,
78442
+ endKey: effectiveDateRange.endKey,
78443
+ trendMode: effectiveTrendMode,
77671
78444
  comparisonStrategy,
77672
- isLiveScope: isCurrentWeekToDateRange
78445
+ isLiveScope,
78446
+ enabled: scopedLineIds.length > 0 && isShiftScopeResolved
77673
78447
  });
77674
78448
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-h-screen bg-slate-50 w-full font-sans", children: [
77675
78449
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -77678,6 +78452,8 @@ var PlantHeadView = () => {
77678
78452
  dateRange,
77679
78453
  displayDateRange: headerDateRange,
77680
78454
  trendMode,
78455
+ isLiveScope,
78456
+ liveShiftName: isLiveScope && trendMode !== "all" ? trendMode : null,
77681
78457
  lineOptions,
77682
78458
  supervisorOptions,
77683
78459
  selectedSupervisorId,
@@ -77700,7 +78476,7 @@ var PlantHeadView = () => {
77700
78476
  store,
77701
78477
  supervisorsByLineId,
77702
78478
  onViewAll: handleViewAllPoorestPerformers,
77703
- onLineClick: handleOpenLineMonthlyHistory
78479
+ onLineClick: handleOpenLineDetails
77704
78480
  }
77705
78481
  ),
77706
78482
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -77717,7 +78493,8 @@ var PlantHeadView = () => {
77717
78493
  {
77718
78494
  store,
77719
78495
  dateRange,
77720
- appTimezone
78496
+ appTimezone,
78497
+ hourlyLabelStartTime
77721
78498
  }
77722
78499
  ),
77723
78500
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -78507,6 +79284,7 @@ exports.formatRelativeTime = formatRelativeTime;
78507
79284
  exports.formatTimeInZone = formatTimeInZone;
78508
79285
  exports.fromUrlFriendlyName = fromUrlFriendlyName;
78509
79286
  exports.getActionDisplayName = getActionDisplayName;
79287
+ exports.getActiveShift = getActiveShift;
78510
79288
  exports.getAllLineDisplayNames = getAllLineDisplayNames;
78511
79289
  exports.getAllThreadMessages = getAllThreadMessages;
78512
79290
  exports.getAllWorkspaceDisplayNamesAsync = getAllWorkspaceDisplayNamesAsync;
@@ -78578,6 +79356,7 @@ exports.isEfficiencyOnTrack = isEfficiencyOnTrack;
78578
79356
  exports.isFactoryScopedRole = isFactoryScopedRole;
78579
79357
  exports.isFullMonthRange = isFullMonthRange;
78580
79358
  exports.isLegacyConfiguration = isLegacyConfiguration;
79359
+ exports.isLoopbackHostname = isLoopbackHostname;
78581
79360
  exports.isPrefetchError = isPrefetchError;
78582
79361
  exports.isRecentFlowVideoGridMetricMode = isRecentFlowVideoGridMetricMode;
78583
79362
  exports.isSafari = isSafari;
@@ -78619,6 +79398,7 @@ exports.resetSubscriptionManager = resetSubscriptionManager;
78619
79398
  exports.s3VideoPreloader = s3VideoPreloader;
78620
79399
  exports.setSentryUserContext = setSentryUserContext;
78621
79400
  exports.setSentryWorkspaceContext = setSentryWorkspaceContext;
79401
+ exports.shouldEnableLocalDevTestLogin = shouldEnableLocalDevTestLogin;
78622
79402
  exports.shuffleArray = shuffleArray;
78623
79403
  exports.simulateApiDelay = simulateApiDelay;
78624
79404
  exports.skuService = skuService;