@optifye/dashboard-core 6.12.46 → 6.12.47

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
@@ -14900,6 +14900,7 @@ var transformMonitorWorkspaceMetrics = ({
14900
14900
  leaderboard_value: item.leaderboard_value ?? null,
14901
14901
  avg_recent_flow: item.avg_recent_flow ?? null,
14902
14902
  recent_flow_percent: item.recent_flow_percent ?? null,
14903
+ recent_flow_hourly: item.recent_flow_hourly ?? null,
14903
14904
  recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
14904
14905
  recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
14905
14906
  recent_flow_computed_at: item.recent_flow_computed_at ?? null,
@@ -38502,6 +38503,114 @@ var getRawVideoGridMetricValue = (workspace) => {
38502
38503
  };
38503
38504
  var hasIncomingWipMapping = (workspace) => Boolean(workspace.incoming_wip_buffer_name);
38504
38505
  var getVideoGridWorkspaceKey = (workspace) => workspace.workspace_uuid || `${workspace.line_id || "unknown"}:${workspace.workspace_name || "unknown"}`;
38506
+ var getWorstPerformanceWorkstationLimit = (totalWorkstations) => {
38507
+ if (!Number.isFinite(totalWorkstations) || totalWorkstations <= 0) return 0;
38508
+ if (totalWorkstations === 2) return 1;
38509
+ if (totalWorkstations === 4) return 2;
38510
+ return Math.min(3, Math.max(1, Math.floor(totalWorkstations)));
38511
+ };
38512
+ var coerceRecentFlowHourlyValue = (value) => {
38513
+ if (typeof value === "number" && Number.isFinite(value)) {
38514
+ return value;
38515
+ }
38516
+ if (typeof value === "string") {
38517
+ const trimmed = value.trim();
38518
+ if (!trimmed || trimmed.toLowerCase() === "x") return null;
38519
+ const parsed = Number(trimmed);
38520
+ return Number.isFinite(parsed) ? parsed : null;
38521
+ }
38522
+ return null;
38523
+ };
38524
+ var collectRecentFlowHourlyValues = (hourly) => {
38525
+ if (!hourly || typeof hourly !== "object" || Array.isArray(hourly)) {
38526
+ return [];
38527
+ }
38528
+ const values = [];
38529
+ Object.values(hourly).forEach((rawSlotValues) => {
38530
+ if (Array.isArray(rawSlotValues)) {
38531
+ rawSlotValues.forEach((rawValue) => {
38532
+ const value = coerceRecentFlowHourlyValue(rawValue);
38533
+ if (value !== null) values.push(value);
38534
+ });
38535
+ return;
38536
+ }
38537
+ if (rawSlotValues && typeof rawSlotValues === "object") {
38538
+ Object.values(rawSlotValues).forEach((rawValue) => {
38539
+ const value = coerceRecentFlowHourlyValue(rawValue);
38540
+ if (value !== null) values.push(value);
38541
+ });
38542
+ }
38543
+ });
38544
+ return values;
38545
+ };
38546
+ var getAverageRecentFlowHourlyPercent = (workspace) => {
38547
+ const values = collectRecentFlowHourlyValues(workspace.recent_flow_hourly);
38548
+ if (values.length > 0) {
38549
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
38550
+ }
38551
+ if (isFiniteNumber4(workspace.avg_recent_flow)) {
38552
+ return workspace.avg_recent_flow;
38553
+ }
38554
+ return isFiniteNumber4(workspace.recent_flow_percent) ? workspace.recent_flow_percent : null;
38555
+ };
38556
+ var getAssemblyCycleOverStandardRatio = (workspace) => {
38557
+ const medianCycleTime = toFiniteNumberOrNull(workspace.avg_cycle_time);
38558
+ const standardCycleTime = toFiniteNumberOrNull(workspace.ideal_cycle_time);
38559
+ if (medianCycleTime === null || standardCycleTime === null || medianCycleTime <= 0 || standardCycleTime <= 0) {
38560
+ return null;
38561
+ }
38562
+ return medianCycleTime / standardCycleTime;
38563
+ };
38564
+ var getUptimeWorstPerformanceValue = (workspace) => toFiniteNumberOrNull(workspace.efficiency);
38565
+ var isWorstPerformanceEligible = (workspace) => isValidAggregateEfficiency(workspace.monitoring_mode, workspace.efficiency);
38566
+ var isAssemblyLineGroup = (lineWorkspaces) => (
38567
+ // `assembly_enabled` is denormalized from `lines.assembly` onto every
38568
+ // live-monitor workspace row; it is not a workstation-level flag.
38569
+ lineWorkspaces.some((workspace) => workspace.assembly_enabled === true)
38570
+ );
38571
+ var getWorstPerformanceLineMode = (lineWorkspaces) => {
38572
+ if (lineWorkspaces.some((workspace) => normalizeMonitoringMode(workspace.monitoring_mode) === "uptime")) {
38573
+ return "uptime";
38574
+ }
38575
+ return isAssemblyLineGroup(lineWorkspaces) ? "assembly" : "flow";
38576
+ };
38577
+ var getEligibleWorstPerformanceCandidates = (lineWorkspaces) => lineWorkspaces.map((workspace, index) => ({ workspace, index })).filter(({ workspace }) => isWorstPerformanceEligible(workspace));
38578
+ var selectWorstPerformanceWorkspaceIds = (workspaces) => {
38579
+ const workspacesByLine = /* @__PURE__ */ new Map();
38580
+ workspaces.forEach((workspace) => {
38581
+ const lineKey = workspace.line_id || "unknown";
38582
+ const lineWorkspaces = workspacesByLine.get(lineKey) || [];
38583
+ lineWorkspaces.push(workspace);
38584
+ workspacesByLine.set(lineKey, lineWorkspaces);
38585
+ });
38586
+ const selected = /* @__PURE__ */ new Set();
38587
+ workspacesByLine.forEach((lineWorkspaces) => {
38588
+ const limit = getWorstPerformanceWorkstationLimit(lineWorkspaces.length);
38589
+ if (limit <= 0) return;
38590
+ const eligibleCandidates = getEligibleWorstPerformanceCandidates(lineWorkspaces);
38591
+ if (eligibleCandidates.length === 0) return;
38592
+ const lineMode = getWorstPerformanceLineMode(lineWorkspaces);
38593
+ if (lineMode === "uptime") {
38594
+ eligibleCandidates.map((candidate) => ({
38595
+ ...candidate,
38596
+ efficiency: getUptimeWorstPerformanceValue(candidate.workspace)
38597
+ })).filter((entry) => entry.efficiency !== null).sort((left, right) => left.efficiency - right.efficiency || left.workspace.workspace_name.localeCompare(right.workspace.workspace_name, void 0, { numeric: true }) || left.index - right.index).slice(0, limit).forEach((entry) => selected.add(getVideoGridWorkspaceKey(entry.workspace)));
38598
+ return;
38599
+ }
38600
+ if (lineMode === "assembly") {
38601
+ eligibleCandidates.map((candidate) => ({
38602
+ ...candidate,
38603
+ ratio: getAssemblyCycleOverStandardRatio(candidate.workspace)
38604
+ })).filter((entry) => entry.ratio !== null).sort((left, right) => right.ratio - left.ratio || left.workspace.workspace_name.localeCompare(right.workspace.workspace_name, void 0, { numeric: true }) || left.index - right.index).slice(0, limit).forEach((entry) => selected.add(getVideoGridWorkspaceKey(entry.workspace)));
38605
+ return;
38606
+ }
38607
+ eligibleCandidates.map((candidate) => ({
38608
+ ...candidate,
38609
+ averageFlow: getAverageRecentFlowHourlyPercent(candidate.workspace)
38610
+ })).filter((entry) => entry.averageFlow !== null).sort((left, right) => left.averageFlow - right.averageFlow || left.workspace.workspace_name.localeCompare(right.workspace.workspace_name, void 0, { numeric: true }) || left.index - right.index).slice(0, limit).forEach((entry) => selected.add(getVideoGridWorkspaceKey(entry.workspace)));
38611
+ });
38612
+ return selected;
38613
+ };
38505
38614
  var getVideoGridBlueComparisonGroupKey = (workspace) => {
38506
38615
  const factoryAreaId = typeof workspace.factory_area_id === "string" ? workspace.factory_area_id.trim() : "";
38507
38616
  if (factoryAreaId && workspace.factory_area_enabled === true) {
@@ -38794,6 +38903,7 @@ var VideoCard = React148__namespace.default.memo(({
38794
38903
  displayMinuteBucket,
38795
38904
  displayName,
38796
38905
  isBlueBest = false,
38906
+ isWorstPerformance = false,
38797
38907
  lastSeenLabel,
38798
38908
  hasRecentHealthSignal: hasRecentHealthSignal2 = false,
38799
38909
  onMouseEnter,
@@ -38832,6 +38942,8 @@ var VideoCard = React148__namespace.default.memo(({
38832
38942
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "blue" ? "bg-[#0EA5E9]/30" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
38833
38943
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "blue" ? "bg-[#0EA5E9]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
38834
38944
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "blue" ? "Best" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
38945
+ const worstMarkerClassName = compact ? "left-1.5 top-1.5 gap-1.5 px-2 py-1 text-[11px]" : "left-2 top-2 gap-2 px-3 py-1.5 text-xs";
38946
+ const statusBadgesPositionClass = isWorstPerformance ? compact ? "top-8 left-1.5 gap-1" : "top-10 left-2 gap-1.5" : compact ? "top-1.5 left-1.5 gap-1" : "top-2 left-2 gap-1.5";
38835
38947
  const trendInfo = workspace.trend !== void 0 ? getTrendArrowAndColor(workspace.trend) : null;
38836
38948
  const handleClick = React148.useCallback(() => {
38837
38949
  trackCoreEvent("Workspace Card Clicked", {
@@ -38850,6 +38962,7 @@ var VideoCard = React148__namespace.default.memo(({
38850
38962
  {
38851
38963
  className: `workspace-card relative bg-gray-950 rounded-md overflow-hidden cursor-pointer shadow-[0_1px_3px_rgba(15,23,42,0.16),0_0_0_1px_rgba(255,255,255,0.06)] transition-[box-shadow] duration-200 hover:shadow-[0_10px_24px_rgba(15,23,42,0.28),0_0_0_1px_rgba(255,255,255,0.10)] active:shadow-[0_1px_3px_rgba(15,23,42,0.16),0_0_0_1px_rgba(255,255,255,0.06)] touch-manipulation ${className}`,
38852
38964
  style: { width: "100%", height: "100%" },
38965
+ "data-worst-performance-highlight": isWorstPerformance ? "true" : void 0,
38853
38966
  onClick: handleClick,
38854
38967
  onMouseEnter,
38855
38968
  onMouseLeave,
@@ -38911,7 +39024,7 @@ var VideoCard = React148__namespace.default.memo(({
38911
39024
  "div",
38912
39025
  {
38913
39026
  "data-testid": "video-card-status-badges",
38914
- className: `absolute ${compact ? "top-1.5 left-1.5 gap-1" : "top-2 left-2 gap-1.5"} z-30 flex items-center`,
39027
+ className: `absolute ${statusBadgesPositionClass} z-30 flex items-center`,
38915
39028
  children: statusBadges.map((badge, index) => {
38916
39029
  const presentation = getIdleReasonPresentation({
38917
39030
  label: badge.label,
@@ -38962,6 +39075,17 @@ var VideoCard = React148__namespace.default.memo(({
38962
39075
  })
38963
39076
  }
38964
39077
  ),
39078
+ isWorstPerformance && /* @__PURE__ */ jsxRuntime.jsxs(
39079
+ "div",
39080
+ {
39081
+ "data-testid": "video-card-worst-performance-marker",
39082
+ className: `pointer-events-none absolute z-[65] inline-flex items-center rounded-md bg-[#1A0B09]/95 border border-[#E34329]/60 font-semibold tracking-wide text-white shadow-xl ${worstMarkerClassName}`,
39083
+ children: [
39084
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: `${compact ? "h-3.5 w-3.5" : "h-4 w-4"} text-[#E34329]`, "aria-hidden": "true" }),
39085
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Overall Underperformer" })
39086
+ ]
39087
+ }
39088
+ ),
38965
39089
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute bottom-0 left-0 right-0 ${compact ? "h-0.5" : "h-1"} bg-black/50 z-30`, children: /* @__PURE__ */ jsxRuntime.jsx(
38966
39090
  "div",
38967
39091
  {
@@ -39010,6 +39134,9 @@ var VideoCard = React148__namespace.default.memo(({
39010
39134
  if (prevProps.isBlueBest !== nextProps.isBlueBest) {
39011
39135
  return false;
39012
39136
  }
39137
+ if (prevProps.isWorstPerformance !== nextProps.isWorstPerformance) {
39138
+ return false;
39139
+ }
39013
39140
  if (prevProps.lastSeenLabel !== nextProps.lastSeenLabel) {
39014
39141
  return false;
39015
39142
  }
@@ -39052,6 +39179,7 @@ var hasRecentHealthSignal = (lastHeartbeat) => {
39052
39179
  var VideoGridView = React148__namespace.default.memo(({
39053
39180
  workspaces,
39054
39181
  blueComparisonWorkspaces,
39182
+ worstPerformanceWorkspaceIds = [],
39055
39183
  selectedLine,
39056
39184
  className = "",
39057
39185
  legend,
@@ -39059,6 +39187,9 @@ var VideoGridView = React148__namespace.default.memo(({
39059
39187
  videoStreamsByWorkspaceId,
39060
39188
  videoStreamsLoading = false,
39061
39189
  displayNames = {},
39190
+ lineOrder = [],
39191
+ activeSlideshowLineId = null,
39192
+ displayMode = "all",
39062
39193
  onWorkspaceHover,
39063
39194
  onWorkspaceHoverEnd
39064
39195
  }) => {
@@ -39181,6 +39312,48 @@ var VideoGridView = React148__namespace.default.memo(({
39181
39312
  });
39182
39313
  }, [filteredWorkspaces]);
39183
39314
  const blueWinnerIds = React148.useMemo(() => selectVideoGridBlueWinnerIds(blueComparisonWorkspaces || sortedWorkspaces, effectiveLegend), [blueComparisonWorkspaces, sortedWorkspaces, effectiveLegend]);
39315
+ const worstPerformanceIdSet = React148.useMemo(
39316
+ () => new Set(worstPerformanceWorkspaceIds),
39317
+ [worstPerformanceWorkspaceIds]
39318
+ );
39319
+ const modeBaseWorkspaces = React148.useMemo(() => {
39320
+ if (displayMode !== "red_only") {
39321
+ return sortedWorkspaces;
39322
+ }
39323
+ return sortedWorkspaces.filter((workspace) => getVideoGridColorState(workspace, effectiveLegend, blueWinnerIds) === "red");
39324
+ }, [blueWinnerIds, displayMode, effectiveLegend, sortedWorkspaces]);
39325
+ const slideshowLines = React148.useMemo(() => {
39326
+ const linesWithWorkspaces = new Set(
39327
+ sortedWorkspaces.map((workspace) => workspace.line_id).filter((lineId) => Boolean(lineId))
39328
+ );
39329
+ if (lineOrder.length > 0) {
39330
+ return lineOrder.filter((lineId) => linesWithWorkspaces.has(lineId));
39331
+ }
39332
+ const orderedLineIds = [];
39333
+ const seenLineIds = /* @__PURE__ */ new Set();
39334
+ for (const workspace of sortedWorkspaces) {
39335
+ if (!workspace.line_id || seenLineIds.has(workspace.line_id)) {
39336
+ continue;
39337
+ }
39338
+ seenLineIds.add(workspace.line_id);
39339
+ orderedLineIds.push(workspace.line_id);
39340
+ }
39341
+ return orderedLineIds;
39342
+ }, [lineOrder, sortedWorkspaces]);
39343
+ const currentSlideshowLineId = displayMode === "slideshow" && slideshowLines.length > 0 ? activeSlideshowLineId && slideshowLines.includes(activeSlideshowLineId) ? activeSlideshowLineId : slideshowLines[0] : null;
39344
+ const displayWorkspaces = React148.useMemo(() => {
39345
+ if (displayMode === "red_only") {
39346
+ return modeBaseWorkspaces;
39347
+ }
39348
+ if (displayMode !== "slideshow") {
39349
+ return sortedWorkspaces;
39350
+ }
39351
+ if (slideshowLines.length === 0) {
39352
+ return [];
39353
+ }
39354
+ const currentLineId = currentSlideshowLineId || slideshowLines[0];
39355
+ return sortedWorkspaces.filter((workspace) => workspace.line_id === currentLineId);
39356
+ }, [currentSlideshowLineId, displayMode, modeBaseWorkspaces, slideshowLines, sortedWorkspaces]);
39184
39357
  const streamsResolvedForWorkspaceSet = resolvedStreamWorkspaceKey === workspaceIdsKey;
39185
39358
  const resolveWorkspaceDisplayName = React148.useCallback((workspace) => {
39186
39359
  return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
@@ -39191,7 +39364,7 @@ var VideoGridView = React148__namespace.default.memo(({
39191
39364
  const rawContainerWidth = containerRef.current.clientWidth;
39192
39365
  const containerWidth = rawContainerWidth - containerPadding;
39193
39366
  const containerHeight = containerRef.current.clientHeight - containerPadding;
39194
- const count = filteredWorkspaces.length;
39367
+ const count = displayWorkspaces.length;
39195
39368
  if (count === 0) {
39196
39369
  setGridCols(1);
39197
39370
  setGridRows(1);
@@ -39242,7 +39415,7 @@ var VideoGridView = React148__namespace.default.memo(({
39242
39415
  setGridCols(bestCols);
39243
39416
  setGridRows(rows);
39244
39417
  setIsMobileScrollableGrid(shouldUseMobileScroll);
39245
- }, [filteredWorkspaces.length, selectedLine]);
39418
+ }, [displayWorkspaces.length, selectedLine]);
39246
39419
  React148.useEffect(() => {
39247
39420
  calculateOptimalGrid();
39248
39421
  const handleResize = () => calculateOptimalGrid();
@@ -39276,7 +39449,7 @@ var VideoGridView = React148__namespace.default.memo(({
39276
39449
  return () => {
39277
39450
  observerRef.current?.disconnect();
39278
39451
  };
39279
- }, [filteredWorkspaces]);
39452
+ }, [displayWorkspaces]);
39280
39453
  const prewarmClipsInit = React148.useCallback((workspace) => {
39281
39454
  if (!dashboardConfig?.s3Config) return;
39282
39455
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
@@ -39362,7 +39535,7 @@ var VideoGridView = React148__namespace.default.memo(({
39362
39535
  });
39363
39536
  }, []);
39364
39537
  const workspaceCards = React148.useMemo(() => {
39365
- return sortedWorkspaces.map((workspace) => {
39538
+ return displayWorkspaces.map((workspace) => {
39366
39539
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
39367
39540
  const workspaceKey = `${workspace.line_id || "unknown"}-${workspaceId}`;
39368
39541
  const isVisible = visibleWorkspaces.has(workspaceId);
@@ -39390,12 +39563,14 @@ var VideoGridView = React148__namespace.default.memo(({
39390
39563
  shouldPlay,
39391
39564
  lastSeenLabel,
39392
39565
  hasRecentHealthSignal: hasRecentHealthSignal(workspaceHealth?.lastHeartbeat),
39393
- isBlueBest: blueWinnerIds.has(getVideoGridWorkspaceKey(workspace))
39566
+ isBlueBest: blueWinnerIds.has(getVideoGridWorkspaceKey(workspace)),
39567
+ isWorstPerformance: worstPerformanceIdSet.has(getVideoGridWorkspaceKey(workspace))
39394
39568
  };
39395
39569
  });
39396
39570
  }, [
39397
- sortedWorkspaces,
39571
+ displayWorkspaces,
39398
39572
  blueWinnerIds,
39573
+ worstPerformanceIdSet,
39399
39574
  visibleWorkspaces,
39400
39575
  getWorkspaceCropping,
39401
39576
  videoStreamsByWorkspaceId,
@@ -39443,6 +39618,7 @@ var VideoGridView = React148__namespace.default.memo(({
39443
39618
  displayMinuteBucket,
39444
39619
  compact: !selectedLine,
39445
39620
  isBlueBest: card.isBlueBest,
39621
+ isWorstPerformance: card.isWorstPerformance,
39446
39622
  onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(card.workspaceId) : void 0,
39447
39623
  onMouseLeave: onWorkspaceHoverEnd ? () => onWorkspaceHoverEnd(card.workspaceId) : void 0
39448
39624
  }
@@ -39468,9 +39644,28 @@ var VideoGridView = React148__namespace.default.memo(({
39468
39644
  "data-testid": "video-grid-scroll-container",
39469
39645
  "data-mobile-scrollable": isMobileScrollableGrid ? "true" : "false",
39470
39646
  className: `absolute inset-0 w-full overflow-x-hidden px-1 py-1 sm:px-2 sm:py-2 ${isMobileScrollableGrid ? "overflow-y-auto" : "overflow-hidden"}`,
39471
- children: /* @__PURE__ */ jsxRuntime.jsx(
39472
- "div",
39647
+ children: /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { mode: "popLayout", children: displayMode === "red_only" && workspaceCards.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
39648
+ motion.div,
39473
39649
  {
39650
+ initial: { opacity: 0, scale: 0.95 },
39651
+ animate: { opacity: 1, scale: 1 },
39652
+ exit: { opacity: 0, scale: 0.95 },
39653
+ transition: { duration: 0.3 },
39654
+ "data-testid": "video-grid-empty-red-cameras",
39655
+ className: "flex h-full min-h-[240px] flex-col items-center justify-center rounded-xl border border-emerald-200 bg-emerald-50/70 px-6 text-center",
39656
+ children: [
39657
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base font-semibold text-emerald-900", children: "No red cameras right now" }),
39658
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1 max-w-md text-sm text-emerald-700", children: "The selected line filter has no cameras in the red state. Switch back to all cameras to keep monitoring the full floor." })
39659
+ ]
39660
+ },
39661
+ "empty-red"
39662
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
39663
+ motion.div,
39664
+ {
39665
+ initial: displayMode === "slideshow" ? { opacity: 0, x: 100 } : { opacity: 0 },
39666
+ animate: displayMode === "slideshow" ? { opacity: 1, x: 0 } : { opacity: 1 },
39667
+ exit: displayMode === "slideshow" ? { opacity: 0, x: -100 } : { opacity: 0 },
39668
+ transition: { duration: 0.5, ease: [0.32, 0.72, 0, 1] },
39474
39669
  "data-testid": "video-grid-layout",
39475
39670
  className: `grid min-w-0 w-full gap-1.5 sm:gap-2 ${isMobileScrollableGrid ? "content-start" : "h-full"}`,
39476
39671
  style: {
@@ -39482,8 +39677,9 @@ var VideoGridView = React148__namespace.default.memo(({
39482
39677
  card,
39483
39678
  isMobileScrollableGrid ? "workspace-card relative min-w-0 w-full aspect-video min-h-[92px]" : "workspace-card relative min-w-0 w-full h-full"
39484
39679
  ))
39485
- }
39486
- )
39680
+ },
39681
+ `grid-${displayMode === "slideshow" ? currentSlideshowLineId : "standard"}`
39682
+ ) })
39487
39683
  }
39488
39684
  ) });
39489
39685
  });
@@ -47197,6 +47393,7 @@ function useClipsRealtimeUpdates({
47197
47393
  hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
47198
47394
  };
47199
47395
  }
47396
+ var LOW_EFFICIENCY_CATEGORY_ID = "recent_flow_red_streak";
47200
47397
  var parseFiniteNumber2 = (value) => {
47201
47398
  if (typeof value === "number" && Number.isFinite(value)) {
47202
47399
  return value;
@@ -47455,6 +47652,7 @@ var BottlenecksContent = ({
47455
47652
  totalOutput,
47456
47653
  enabled: isEffectiveShiftReady
47457
47654
  });
47655
+ const isLowMomentsCategoryAvailable = React148.useMemo(() => Array.isArray(clipTypes) && clipTypes.some((type) => type?.type === LOW_EFFICIENCY_CATEGORY_ID || type?.id === LOW_EFFICIENCY_CATEGORY_ID), [clipTypes]);
47458
47656
  console.log("[BottlenecksContent] Clip types data:", {
47459
47657
  clipTypes,
47460
47658
  clipTypesLength: clipTypes?.length,
@@ -47505,20 +47703,20 @@ var BottlenecksContent = ({
47505
47703
  if (initialFilter) {
47506
47704
  return;
47507
47705
  }
47508
- const redStreakType = "recent_flow_red_streak";
47509
- if (dynamicCounts[redStreakType] > 0) {
47706
+ const redStreakType = LOW_EFFICIENCY_CATEGORY_ID;
47707
+ if (isLowMomentsCategoryAvailable && dynamicCounts[redStreakType] > 0) {
47510
47708
  setInitialFilter(redStreakType);
47511
47709
  setActiveFilter(redStreakType);
47512
47710
  activeFilterRef.current = redStreakType;
47513
47711
  return;
47514
47712
  }
47515
- if (defaultCategory) {
47713
+ if (defaultCategory && (defaultCategory !== LOW_EFFICIENCY_CATEGORY_ID || isLowMomentsCategoryAvailable)) {
47516
47714
  setInitialFilter(defaultCategory);
47517
47715
  setActiveFilter(defaultCategory);
47518
47716
  activeFilterRef.current = defaultCategory;
47519
47717
  return;
47520
47718
  }
47521
- }, [clipTypes, dynamicCounts, defaultCategory, initialFilter]);
47719
+ }, [clipTypes, dynamicCounts, defaultCategory, initialFilter, isLowMomentsCategoryAvailable]);
47522
47720
  const mergedCounts = React148.useMemo(() => {
47523
47721
  return { ...dynamicCounts };
47524
47722
  }, [dynamicCounts]);
@@ -47737,7 +47935,7 @@ var BottlenecksContent = ({
47737
47935
  };
47738
47936
  const [cycleClips, redFlowClips, idleClips] = await Promise.all([
47739
47937
  fetchCategoryMetadata("cycle_completion"),
47740
- fetchCategoryMetadata("recent_flow_red_streak"),
47938
+ isLowMomentsCategoryAvailable ? fetchCategoryMetadata(LOW_EFFICIENCY_CATEGORY_ID) : Promise.resolve([]),
47741
47939
  fetchCategoryMetadata("idle_time")
47742
47940
  ]);
47743
47941
  const allClips = [...cycleClips, ...redFlowClips, ...idleClips].sort(
@@ -47751,7 +47949,7 @@ var BottlenecksContent = ({
47751
47949
  }
47752
47950
  };
47753
47951
  fetchTriageClips();
47754
- }, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, supabase, isEffectiveShiftReady]);
47952
+ }, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, supabase, isEffectiveShiftReady, isLowMomentsCategoryAvailable]);
47755
47953
  React148.useEffect(() => {
47756
47954
  if (!idleTimeVlmEnabled || !triageMode || triageClips.length === 0) {
47757
47955
  return;
@@ -47789,7 +47987,7 @@ var BottlenecksContent = ({
47789
47987
  }, [idleTimeVlmEnabled, triageClips, triageMode, getAuthToken4]);
47790
47988
  React148.useEffect(() => {
47791
47989
  if (s3ClipsService && (mergedCounts[activeFilter] || 0) > 0) {
47792
- if (activeFilter === "recent_flow_red_streak") {
47990
+ if (activeFilter === LOW_EFFICIENCY_CATEGORY_ID) {
47793
47991
  return;
47794
47992
  }
47795
47993
  if (firstClip && firstClip.type === activeFilter) {
@@ -47812,12 +48010,12 @@ var BottlenecksContent = ({
47812
48010
  return ["fast-cycles", "slow-cycles", "longest-idles"].includes(categoryId);
47813
48011
  }, [isFastSlowClipFiltersEnabled]);
47814
48012
  const shouldUseMetadataNavigation = React148.useCallback((categoryId) => {
47815
- return isPercentileCategory(categoryId) || categoryId === "recent_flow_red_streak" || categoryId === "idle_time" && idleClipSort === "idle_duration_desc";
48013
+ return isPercentileCategory(categoryId) || categoryId === LOW_EFFICIENCY_CATEGORY_ID || categoryId === "idle_time" && idleClipSort === "idle_duration_desc";
47816
48014
  }, [idleClipSort, isPercentileCategory]);
47817
48015
  const getMetadataCacheKey = React148.useCallback((categoryId) => {
47818
- const sortKey = categoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest";
47819
- return `${categoryId}-${effectiveDateString}-${effectiveShiftId}-${snapshotDateTime ?? "nosnap"}-${snapshotClipId ?? "nosnap"}-${sortKey}`;
47820
- }, [effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId, idleClipSort]);
48016
+ const sortKey = categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest";
48017
+ return `${categoryId}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-${snapshotDateTime ?? "nosnap"}-${snapshotClipId ?? "nosnap"}-${sortKey}`;
48018
+ }, [workspaceId, effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId, idleClipSort]);
47821
48019
  const setVisibleCategoryMetadata = React148.useCallback((categoryId, clips) => {
47822
48020
  if (activeFilterRef.current !== categoryId) {
47823
48021
  return false;
@@ -47825,7 +48023,7 @@ var BottlenecksContent = ({
47825
48023
  categoryMetadataRef.current = clips;
47826
48024
  setCategoryMetadata(clips);
47827
48025
  setCategoryMetadataCategoryId(clips.length > 0 ? categoryId : null);
47828
- setCategoryMetadataSort(clips.length > 0 ? categoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest" : null);
48026
+ setCategoryMetadataSort(clips.length > 0 ? categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest" : null);
47829
48027
  return true;
47830
48028
  }, [idleClipSort]);
47831
48029
  const applyMetadataSnapshot = React148.useCallback((categoryId, clips, total) => {
@@ -47878,7 +48076,8 @@ var BottlenecksContent = ({
47878
48076
  if (activeFilter !== "fast-cycles" && activeFilter !== "slow-cycles") {
47879
48077
  return;
47880
48078
  }
47881
- const fallbackFilter = ["cycle_completion", "recent_flow_red_streak", "idle_time"].find((type) => (dynamicCounts[type] || 0) > 0) || clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0)?.type || clipTypes[0]?.type;
48079
+ const lowMomentsFallback = isLowMomentsCategoryAvailable ? LOW_EFFICIENCY_CATEGORY_ID : null;
48080
+ const fallbackFilter = ["cycle_completion", lowMomentsFallback, "idle_time"].filter((type) => Boolean(type)).find((type) => (dynamicCounts[type] || 0) > 0) || clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0)?.type || clipTypes[0]?.type;
47882
48081
  if (!fallbackFilter || fallbackFilter === activeFilter) {
47883
48082
  return;
47884
48083
  }
@@ -47887,7 +48086,25 @@ var BottlenecksContent = ({
47887
48086
  setCategoryMetadataSort(null);
47888
48087
  categoryMetadataRef.current = [];
47889
48088
  updateActiveFilter(fallbackFilter);
47890
- }, [isFastSlowClipFiltersEnabled, activeFilter, dynamicCounts, clipTypes, updateActiveFilter]);
48089
+ }, [isFastSlowClipFiltersEnabled, activeFilter, dynamicCounts, clipTypes, updateActiveFilter, isLowMomentsCategoryAvailable]);
48090
+ React148.useEffect(() => {
48091
+ if (activeFilter !== LOW_EFFICIENCY_CATEGORY_ID || isLowMomentsCategoryAvailable) {
48092
+ return;
48093
+ }
48094
+ const fallbackFilter = clipTypes.find((type) => type.type !== LOW_EFFICIENCY_CATEGORY_ID && (dynamicCounts[type.type] || 0) > 0)?.type || clipTypes.find((type) => type.type !== LOW_EFFICIENCY_CATEGORY_ID)?.type || "";
48095
+ setCategoryMetadata([]);
48096
+ setCategoryMetadataCategoryId(null);
48097
+ setCategoryMetadataSort(null);
48098
+ categoryMetadataRef.current = [];
48099
+ if (fallbackFilter) {
48100
+ setInitialFilter(fallbackFilter);
48101
+ updateActiveFilter(fallbackFilter);
48102
+ } else {
48103
+ setInitialFilter("");
48104
+ updateActiveFilter("");
48105
+ setIsCategoryLoading(false);
48106
+ }
48107
+ }, [activeFilter, isLowMomentsCategoryAvailable, clipTypes, dynamicCounts, updateActiveFilter]);
47891
48108
  React148.useCallback((categoryId) => {
47892
48109
  if (isPercentileCategory(categoryId)) {
47893
48110
  return categoryMetadata.length;
@@ -47939,6 +48156,16 @@ var BottlenecksContent = ({
47939
48156
  }
47940
48157
  return;
47941
48158
  }
48159
+ if (categoryId === LOW_EFFICIENCY_CATEGORY_ID && !isLowMomentsCategoryAvailable) {
48160
+ setCategoryMetadata([]);
48161
+ setCategoryMetadataCategoryId(null);
48162
+ setCategoryMetadataSort(null);
48163
+ categoryMetadataRef.current = [];
48164
+ if (activeFilterRef.current === categoryId) {
48165
+ setIsCategoryLoading(false);
48166
+ }
48167
+ return;
48168
+ }
47942
48169
  if (!isEffectiveShiftReady) {
47943
48170
  console.log("[BottlenecksContent] Skipping metadata load - shift/date not ready");
47944
48171
  if (activeFilterRef.current === categoryId) {
@@ -47950,10 +48177,10 @@ var BottlenecksContent = ({
47950
48177
  const cacheKey = getMetadataCacheKey(categoryId);
47951
48178
  const candidateLowMomentsPrefetch = lowMomentsPrefetch ?? null;
47952
48179
  const lowMomentsPrefetchMatchesScope = Boolean(
47953
- candidateLowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`)
48180
+ candidateLowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`)
47954
48181
  );
47955
- const matchingLowMomentsPrefetch = !forceRefresh && categoryId === "recent_flow_red_streak" && candidateLowMomentsPrefetch && (candidateLowMomentsPrefetch.key === cacheKey || lowMomentsPrefetchMatchesScope) && !candidateLowMomentsPrefetch.loading && Array.isArray(candidateLowMomentsPrefetch.metadata) && candidateLowMomentsPrefetch.metadata.length > 0 ? candidateLowMomentsPrefetch : null;
47956
- const lowMomentsPrefetchInFlight = !forceRefresh && categoryId === "recent_flow_red_streak" && candidateLowMomentsPrefetch && (candidateLowMomentsPrefetch.key === cacheKey || lowMomentsPrefetchMatchesScope) && candidateLowMomentsPrefetch.loading;
48182
+ const matchingLowMomentsPrefetch = !forceRefresh && categoryId === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && candidateLowMomentsPrefetch && (candidateLowMomentsPrefetch.key === cacheKey || lowMomentsPrefetchMatchesScope) && !candidateLowMomentsPrefetch.loading && Array.isArray(candidateLowMomentsPrefetch.metadata) && candidateLowMomentsPrefetch.metadata.length > 0 ? candidateLowMomentsPrefetch : null;
48183
+ const lowMomentsPrefetchInFlight = !forceRefresh && categoryId === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && candidateLowMomentsPrefetch && (candidateLowMomentsPrefetch.key === cacheKey || lowMomentsPrefetchMatchesScope) && candidateLowMomentsPrefetch.loading;
47957
48184
  if (lowMomentsPrefetchInFlight) {
47958
48185
  if (autoLoadFirstVideo && candidateLowMomentsPrefetch?.firstVideo) {
47959
48186
  applyPrefetchedFirstVideo(candidateLowMomentsPrefetch.firstVideo);
@@ -48044,7 +48271,7 @@ var BottlenecksContent = ({
48044
48271
  knownTotal: mergedCounts[categoryId] ?? null,
48045
48272
  snapshotDateTime,
48046
48273
  snapshotClipId,
48047
- sort: categoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
48274
+ sort: categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
48048
48275
  }),
48049
48276
  redirectReason: "session_expired"
48050
48277
  });
@@ -48138,19 +48365,19 @@ var BottlenecksContent = ({
48138
48365
  setIsCategoryLoading(false);
48139
48366
  }
48140
48367
  }
48141
- }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, idleClipSort, supabase, setVisibleCategoryMetadata, lowMomentsPrefetch, applyPrefetchedFirstVideo, applyMetadataSnapshot]);
48368
+ }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, idleClipSort, supabase, setVisibleCategoryMetadata, lowMomentsPrefetch, applyPrefetchedFirstVideo, applyMetadataSnapshot, isLowMomentsCategoryAvailable]);
48142
48369
  React148.useEffect(() => {
48143
- if (activeFilter !== "recent_flow_red_streak") {
48370
+ if (activeFilter !== LOW_EFFICIENCY_CATEGORY_ID || !isLowMomentsCategoryAvailable) {
48144
48371
  return;
48145
48372
  }
48146
- if (!lowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`)) {
48373
+ if (!lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`)) {
48147
48374
  return;
48148
48375
  }
48149
- if (lowMomentsPrefetch.firstVideo && !allVideos.some((video) => video.type === "recent_flow_red_streak")) {
48376
+ if (lowMomentsPrefetch.firstVideo && !allVideos.some((video) => video.type === LOW_EFFICIENCY_CATEGORY_ID)) {
48150
48377
  applyPrefetchedFirstVideo(lowMomentsPrefetch.firstVideo);
48151
48378
  }
48152
48379
  if (lowMomentsPrefetch.metadata.length > 0) {
48153
- applyMetadataSnapshot("recent_flow_red_streak", lowMomentsPrefetch.metadata, lowMomentsPrefetch.total);
48380
+ applyMetadataSnapshot(LOW_EFFICIENCY_CATEGORY_ID, lowMomentsPrefetch.metadata, lowMomentsPrefetch.total);
48154
48381
  if (isMountedRef.current) {
48155
48382
  setIsCategoryLoading(false);
48156
48383
  }
@@ -48162,11 +48389,13 @@ var BottlenecksContent = ({
48162
48389
  }, [
48163
48390
  activeFilter,
48164
48391
  lowMomentsPrefetch,
48392
+ workspaceId,
48165
48393
  effectiveDateString,
48166
48394
  effectiveShiftId,
48167
48395
  allVideos,
48168
48396
  applyPrefetchedFirstVideo,
48169
- applyMetadataSnapshot
48397
+ applyMetadataSnapshot,
48398
+ isLowMomentsCategoryAvailable
48170
48399
  ]);
48171
48400
  React148.useEffect(() => {
48172
48401
  if (previousIdleClipSortRef.current === idleClipSort) {
@@ -48862,11 +49091,11 @@ var BottlenecksContent = ({
48862
49091
  }
48863
49092
  return currentPosition;
48864
49093
  }, [activeFilter, categoryMetadata.length, currentMetadataIndex, currentPosition, shouldUseMetadataNavigation]);
48865
- const prefetchedExplorerMetadata = React148.useMemo(() => activeFilter === "recent_flow_red_streak" && lowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`) && !lowMomentsPrefetch.loading && lowMomentsPrefetch.metadata.length > 0 ? { recent_flow_red_streak: lowMomentsPrefetch.metadata } : activeFilter === "idle_time" && categoryMetadataSort !== idleClipSort ? void 0 : buildPrefetchedExplorerMetadata(
49094
+ const prefetchedExplorerMetadata = React148.useMemo(() => activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && !lowMomentsPrefetch.loading && lowMomentsPrefetch.metadata.length > 0 ? { [LOW_EFFICIENCY_CATEGORY_ID]: lowMomentsPrefetch.metadata } : activeFilter === "idle_time" && categoryMetadataSort !== idleClipSort ? void 0 : buildPrefetchedExplorerMetadata(
48866
49095
  activeFilter,
48867
49096
  categoryMetadataCategoryId,
48868
49097
  categoryMetadata
48869
- ), [activeFilter, categoryMetadata, categoryMetadataCategoryId, categoryMetadataSort, idleClipSort, lowMomentsPrefetch, effectiveDateString, effectiveShiftId]);
49098
+ ), [activeFilter, categoryMetadata, categoryMetadataCategoryId, categoryMetadataSort, idleClipSort, lowMomentsPrefetch, workspaceId, effectiveDateString, effectiveShiftId, isLowMomentsCategoryAvailable]);
48870
49099
  const classificationClipIds = React148.useMemo(() => {
48871
49100
  if (!idleTimeVlmEnabled) {
48872
49101
  return [];
@@ -49639,7 +49868,7 @@ var BottlenecksContent = ({
49639
49868
  prefetchedClipMetadata: prefetchedExplorerMetadata,
49640
49869
  externallyManagedLoadingCategories: {
49641
49870
  recent_flow_red_streak: Boolean(
49642
- activeFilter === "recent_flow_red_streak" && lowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`) && lowMomentsPrefetch.loading
49871
+ activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && lowMomentsPrefetch.loading
49643
49872
  )
49644
49873
  },
49645
49874
  activeCategoryLoading: isCategoryLoading,
@@ -56912,10 +57141,12 @@ WorkspaceGridItem.displayName = "WorkspaceGridItem";
56912
57141
  var WorkspaceGrid = React148__namespace.default.memo(({
56913
57142
  workspaces,
56914
57143
  blueComparisonWorkspaces,
57144
+ worstPerformanceWorkspaceIds = [],
56915
57145
  isPdfMode = false,
56916
57146
  customWorkspacePositions,
56917
57147
  lineNames = {},
56918
57148
  lineOrder = [],
57149
+ activeSlideshowLineId = null,
56919
57150
  factoryView = "factory",
56920
57151
  line2Uuid = "line-2",
56921
57152
  className = "",
@@ -56926,7 +57157,8 @@ var WorkspaceGrid = React148__namespace.default.memo(({
56926
57157
  displayNames = {},
56927
57158
  onWorkspaceHover,
56928
57159
  onWorkspaceHoverEnd,
56929
- toolbarRightContent
57160
+ toolbarRightContent,
57161
+ displayMode = "all"
56930
57162
  }) => {
56931
57163
  const mapViewEnabled = false;
56932
57164
  const [viewMode, setViewMode] = React148.useState(() => {
@@ -56984,13 +57216,16 @@ var WorkspaceGrid = React148__namespace.default.memo(({
56984
57216
  {
56985
57217
  workspaces,
56986
57218
  blueComparisonWorkspaces,
57219
+ worstPerformanceWorkspaceIds,
56987
57220
  lineNames,
56988
57221
  lineOrder,
57222
+ activeSlideshowLineId,
56989
57223
  videoSources,
56990
57224
  videoStreamsByWorkspaceId,
56991
57225
  videoStreamsLoading,
56992
57226
  displayNames,
56993
57227
  legend,
57228
+ displayMode,
56994
57229
  onWorkspaceHover,
56995
57230
  onWorkspaceHoverEnd
56996
57231
  }
@@ -67129,6 +67364,31 @@ var NotificationService = class {
67129
67364
  function createNotificationService(supabaseClient) {
67130
67365
  return new NotificationService(supabaseClient);
67131
67366
  }
67367
+
67368
+ // src/components/dashboard/grid/displayModes.ts
67369
+ var HOME_DISPLAY_MODE_OPTIONS = [
67370
+ {
67371
+ id: "all",
67372
+ label: "Default",
67373
+ description: "Show every camera in the selected line filter."
67374
+ },
67375
+ {
67376
+ id: "red_only",
67377
+ label: "Only red",
67378
+ description: "Only show workstations in red."
67379
+ },
67380
+ {
67381
+ id: "slideshow",
67382
+ label: "Slideshow",
67383
+ description: "Automatically switches between all lines every 15 seconds."
67384
+ },
67385
+ {
67386
+ id: "worst_workstations",
67387
+ label: "Overall Underperformers",
67388
+ description: "Lowest performers based on full day efficiency"
67389
+ }
67390
+ ];
67391
+ var getHomeDisplayModeLabel = (displayMode) => HOME_DISPLAY_MODE_OPTIONS.find((option) => option.id === displayMode)?.label || "Default";
67132
67392
  var KPISection2 = KPISection;
67133
67393
  var DEBUG_DASHBOARD_LOGS3 = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
67134
67394
  var logDebug3 = (...args) => {
@@ -67139,6 +67399,9 @@ var EMPTY_LINE_IDS = [];
67139
67399
  var EMPTY_WORKSPACES = [];
67140
67400
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
67141
67401
  var ALL_GREEN_MILESTONE_DURATION_MS = 6e3;
67402
+ var SLIDESHOW_ROTATION_INTERVAL_MS = 15e3;
67403
+ var SLIDESHOW_IDLE_REQUIRED_MS = 15e3;
67404
+ var SLIDESHOW_ACTIVE_LINE_STORAGE_KEY = "optifye_home_slideshow_active_line_id";
67142
67405
  var ALL_GREEN_CELEBRATION_SEEN_PREFIX = "optifye:all-green-celebration:v1:";
67143
67406
  var ALL_GREEN_MILESTONE_SEEN_PREFIX = "optifye:all-green-milestone:v1:";
67144
67407
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
@@ -67268,6 +67531,31 @@ function HomeView({
67268
67531
  const [isLineSelectorOpen, setIsLineSelectorOpen] = React148.useState(false);
67269
67532
  const [pendingSelectedLineIds, setPendingSelectedLineIds] = React148.useState([]);
67270
67533
  const lineSelectorRef = React148.useRef(null);
67534
+ const [displayMode, setDisplayMode] = React148.useState(() => {
67535
+ if (typeof window === "undefined") {
67536
+ return "all";
67537
+ }
67538
+ try {
67539
+ const savedDisplayMode = sessionStorage.getItem("optifye_home_display_mode");
67540
+ return HOME_DISPLAY_MODE_OPTIONS.some((option) => option.id === savedDisplayMode) ? savedDisplayMode : "all";
67541
+ } catch {
67542
+ return "all";
67543
+ }
67544
+ });
67545
+ const [isDisplayModeMenuOpen, setIsDisplayModeMenuOpen] = React148.useState(false);
67546
+ const displayModeSelectorRef = React148.useRef(null);
67547
+ const readPersistedSlideshowActiveLineId = () => {
67548
+ if (typeof window === "undefined") {
67549
+ return null;
67550
+ }
67551
+ try {
67552
+ return sessionStorage.getItem(SLIDESHOW_ACTIVE_LINE_STORAGE_KEY);
67553
+ } catch {
67554
+ return null;
67555
+ }
67556
+ };
67557
+ const [slideshowActiveLineId, setSlideshowActiveLineId] = React148.useState(() => readPersistedSlideshowActiveLineId());
67558
+ const slideshowLastUserActivityAtRef = React148.useRef(Date.now());
67271
67559
  React148.useEffect(() => {
67272
67560
  if (isLineSelectorOpen) {
67273
67561
  setPendingSelectedLineIds(selectedLineIds);
@@ -67294,6 +67582,13 @@ function HomeView({
67294
67582
  console.warn("Failed to save line filter to sessionStorage:", error);
67295
67583
  }
67296
67584
  }, [selectedLineIds]);
67585
+ React148.useEffect(() => {
67586
+ try {
67587
+ sessionStorage.setItem("optifye_home_display_mode", displayMode);
67588
+ } catch (error) {
67589
+ console.warn("Failed to save display mode to sessionStorage:", error);
67590
+ }
67591
+ }, [displayMode]);
67297
67592
  React148.useEffect(() => {
67298
67593
  if (!isLineSelectorOpen) {
67299
67594
  return;
@@ -67308,6 +67603,20 @@ function HomeView({
67308
67603
  document.removeEventListener("mousedown", handleClickOutside);
67309
67604
  };
67310
67605
  }, [isLineSelectorOpen]);
67606
+ React148.useEffect(() => {
67607
+ if (!isDisplayModeMenuOpen) {
67608
+ return;
67609
+ }
67610
+ const handleClickOutside = (event) => {
67611
+ if (displayModeSelectorRef.current && !displayModeSelectorRef.current.contains(event.target)) {
67612
+ setIsDisplayModeMenuOpen(false);
67613
+ }
67614
+ };
67615
+ document.addEventListener("mousedown", handleClickOutside);
67616
+ return () => {
67617
+ document.removeEventListener("mousedown", handleClickOutside);
67618
+ };
67619
+ }, [isDisplayModeMenuOpen]);
67311
67620
  const primarySelectedLineId = selectedLineIds[0] || defaultHomeLineId;
67312
67621
  const isMultiLineSelection = selectedLineIds.length > 1;
67313
67622
  const selectedLineIdsKey = selectedLineIds.join(",");
@@ -67610,6 +67919,88 @@ function HomeView({
67610
67919
  })),
67611
67920
  [currentWorkspaceMetrics, activeBreakLineIds]
67612
67921
  );
67922
+ const slideshowLineIds = React148.useMemo(() => {
67923
+ const selectedLineIdSetForSlideshow = new Set(selectedLineIds);
67924
+ const linesWithVisibleWorkspaces = new Set(
67925
+ workspaceMetricsWithBreakState.map((workspace) => workspace.line_id).filter((lineId) => Boolean(lineId))
67926
+ );
67927
+ const selectedVisibleLines = visibleLineIds.filter((lineId) => selectedLineIdSetForSlideshow.has(lineId));
67928
+ const selectedLinesWithWorkspaces = selectedVisibleLines.filter((lineId) => linesWithVisibleWorkspaces.has(lineId));
67929
+ if (selectedLinesWithWorkspaces.length > 0) {
67930
+ return selectedLinesWithWorkspaces;
67931
+ }
67932
+ return selectedVisibleLines;
67933
+ }, [selectedLineIds, visibleLineIds, workspaceMetricsWithBreakState]);
67934
+ const slideshowLineIdsKey = slideshowLineIds.join(",");
67935
+ React148.useEffect(() => {
67936
+ if (displayMode !== "slideshow") {
67937
+ return;
67938
+ }
67939
+ slideshowLastUserActivityAtRef.current = Date.now();
67940
+ setSlideshowActiveLineId((previousLineId) => {
67941
+ if (previousLineId && slideshowLineIds.includes(previousLineId)) {
67942
+ return previousLineId;
67943
+ }
67944
+ const persistedLineId = readPersistedSlideshowActiveLineId();
67945
+ if (persistedLineId && slideshowLineIds.includes(persistedLineId)) {
67946
+ return persistedLineId;
67947
+ }
67948
+ if (primarySelectedLineId && slideshowLineIds.includes(primarySelectedLineId)) {
67949
+ return primarySelectedLineId;
67950
+ }
67951
+ return slideshowLineIds[0] || null;
67952
+ });
67953
+ }, [displayMode, primarySelectedLineId, slideshowLineIdsKey]);
67954
+ React148.useEffect(() => {
67955
+ if (typeof window === "undefined") {
67956
+ return;
67957
+ }
67958
+ try {
67959
+ if (slideshowActiveLineId) {
67960
+ sessionStorage.setItem(SLIDESHOW_ACTIVE_LINE_STORAGE_KEY, slideshowActiveLineId);
67961
+ } else {
67962
+ sessionStorage.removeItem(SLIDESHOW_ACTIVE_LINE_STORAGE_KEY);
67963
+ }
67964
+ } catch {
67965
+ }
67966
+ }, [slideshowActiveLineId]);
67967
+ React148.useEffect(() => {
67968
+ if (displayMode !== "slideshow") {
67969
+ return void 0;
67970
+ }
67971
+ const markActivity = () => {
67972
+ slideshowLastUserActivityAtRef.current = Date.now();
67973
+ };
67974
+ const eventOptions = { passive: true };
67975
+ const activityEvents = ["mousemove", "mousedown", "keydown", "touchstart", "wheel"];
67976
+ activityEvents.forEach((eventName) => {
67977
+ window.addEventListener(eventName, markActivity, eventOptions);
67978
+ });
67979
+ return () => {
67980
+ activityEvents.forEach((eventName) => {
67981
+ window.removeEventListener(eventName, markActivity, eventOptions);
67982
+ });
67983
+ };
67984
+ }, [displayMode]);
67985
+ React148.useEffect(() => {
67986
+ if (displayMode !== "slideshow" || slideshowLineIds.length <= 1) {
67987
+ return void 0;
67988
+ }
67989
+ const intervalId = window.setInterval(() => {
67990
+ const nowMs2 = Date.now();
67991
+ if (nowMs2 - slideshowLastUserActivityAtRef.current < SLIDESHOW_IDLE_REQUIRED_MS) {
67992
+ return;
67993
+ }
67994
+ setSlideshowActiveLineId((previousLineId) => {
67995
+ const previousIndex = previousLineId ? slideshowLineIds.indexOf(previousLineId) : -1;
67996
+ const nextIndex = previousIndex >= 0 ? (previousIndex + 1) % slideshowLineIds.length : 0;
67997
+ return slideshowLineIds[nextIndex] || null;
67998
+ });
67999
+ }, SLIDESHOW_ROTATION_INTERVAL_MS);
68000
+ return () => {
68001
+ window.clearInterval(intervalId);
68002
+ };
68003
+ }, [displayMode, slideshowLineIdsKey, slideshowLineIds]);
67613
68004
  const [breakNotificationsDismissed, setBreakNotificationsDismissed] = React148.useState(false);
67614
68005
  React148.useEffect(() => {
67615
68006
  if (currentActiveBreaks.length > 0) {
@@ -67621,6 +68012,11 @@ function HomeView({
67621
68012
  () => workspaceMetricsWithBreakState.filter((workspace) => Boolean(workspace.workspace_uuid || workspace.workspace_name)).length,
67622
68013
  [workspaceMetricsWithBreakState]
67623
68014
  );
68015
+ const worstPerformanceWorkspaceIds = React148.useMemo(
68016
+ () => Array.from(selectWorstPerformanceWorkspaceIds(workspaceMetricsWithBreakState)),
68017
+ [workspaceMetricsWithBreakState]
68018
+ );
68019
+ const activeWorstPerformanceWorkspaceIds = displayMode === "worst_workstations" ? worstPerformanceWorkspaceIds : EMPTY_LINE_IDS;
67624
68020
  const allGreenCelebrationSignature = React148.useMemo(() => {
67625
68021
  const workspaceSignature = workspaceMetricsWithBreakState.map((workspace) => workspace.workspace_uuid || `${workspace.line_id}:${workspace.workspace_name}`).filter(Boolean).sort().join(",");
67626
68022
  return `${selectedLineIds.join(",")}::${workspaceSignature}`;
@@ -68257,6 +68653,10 @@ function HomeView({
68257
68653
  selection_mode: isAllLinesSelection(normalizedLineIds) ? "all" : normalizedLineIds.length === 1 ? "single" : "custom",
68258
68654
  line_name: getLineSelectionLabel(normalizedLineIds)
68259
68655
  });
68656
+ trackCoreEvent("Dashboard Filter Selected", {
68657
+ filter_type: "Line Filter",
68658
+ filter_value: getLineSelectionLabel(normalizedLineIds)
68659
+ });
68260
68660
  }, [factoryViewId, getLineSelectionLabel, getTrackedLineScope, selectedLineIds, selectedLineIdsKey, visibleLineIds]);
68261
68661
  React148.useCallback(() => {
68262
68662
  updateSelectedLineIds(visibleLineIds);
@@ -68311,7 +68711,7 @@ function HomeView({
68311
68711
  "aria-expanded": isLineSelectorOpen,
68312
68712
  "aria-label": "Select lines",
68313
68713
  children: [
68314
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: getLineSelectionLabel(selectedLineIds) }),
68714
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: displayMode === "slideshow" && slideshowActiveLineId ? getLineSelectionLabel([slideshowActiveLineId]) : getLineSelectionLabel(selectedLineIds) }),
68315
68715
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `h-4 w-4 text-slate-400 transition-transform ${isLineSelectorOpen ? "rotate-180" : ""}` })
68316
68716
  ]
68317
68717
  }
@@ -68407,10 +68807,78 @@ function HomeView({
68407
68807
  selectedLineIds,
68408
68808
  pendingSelectedLineIds,
68409
68809
  lineSelectorSignalStatusByLine,
68810
+ displayMode,
68811
+ slideshowActiveLineId,
68410
68812
  visibleLineIds,
68411
68813
  updateSelectedLineIds,
68412
68814
  isAllLinesSelection
68413
68815
  ]);
68816
+ const displayModeSelectorComponent = React148.useMemo(() => /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: displayModeSelectorRef, className: "relative", children: [
68817
+ /* @__PURE__ */ jsxRuntime.jsx(
68818
+ "button",
68819
+ {
68820
+ type: "button",
68821
+ onClick: () => {
68822
+ setIsLineSelectorOpen(false);
68823
+ setIsDisplayModeMenuOpen((previous) => !previous);
68824
+ },
68825
+ className: `inline-flex h-9 w-9 items-center justify-center rounded-md border shadow-sm transition-colors ${displayMode === "all" ? "border-slate-200 bg-white text-slate-500 hover:bg-slate-50 hover:text-slate-700" : "border-blue-200 bg-blue-50 text-blue-600 hover:bg-blue-100"}`,
68826
+ "aria-haspopup": "menu",
68827
+ "aria-expanded": isDisplayModeMenuOpen,
68828
+ "aria-label": `Display mode: ${getHomeDisplayModeLabel(displayMode)}`,
68829
+ title: `Display mode: ${getHomeDisplayModeLabel(displayMode)}`,
68830
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Monitor, { className: "h-4 w-4" })
68831
+ }
68832
+ ),
68833
+ isDisplayModeMenuOpen ? /* @__PURE__ */ jsxRuntime.jsx(
68834
+ "div",
68835
+ {
68836
+ role: "menu",
68837
+ "aria-label": "Display modes",
68838
+ className: "absolute right-0 top-full z-50 mt-1.5 w-64 rounded-md border border-slate-200 bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none",
68839
+ children: HOME_DISPLAY_MODE_OPTIONS.map((option) => {
68840
+ const isSelected = option.id === displayMode;
68841
+ return /* @__PURE__ */ jsxRuntime.jsx(
68842
+ "button",
68843
+ {
68844
+ type: "button",
68845
+ role: "menuitemradio",
68846
+ "aria-checked": isSelected,
68847
+ onClick: () => {
68848
+ const nextSelectedLineIds = option.id === "slideshow" ? visibleLineIds : selectedLineIds;
68849
+ setDisplayMode(option.id);
68850
+ if (option.id === "slideshow") {
68851
+ updateSelectedLineIds(visibleLineIds);
68852
+ }
68853
+ setIsDisplayModeMenuOpen(false);
68854
+ trackCoreEvent("Monitor Display Filter Selected", {
68855
+ filter_name: option.label,
68856
+ filter_id: option.id,
68857
+ previous_display_mode: displayMode,
68858
+ selected_line_ids: nextSelectedLineIds,
68859
+ selected_line_count: nextSelectedLineIds.length,
68860
+ highlighted_workspace_count: option.id === "worst_workstations" ? worstPerformanceWorkspaceIds.length : 0
68861
+ });
68862
+ trackCoreEvent("Dashboard Filter Selected", {
68863
+ filter_type: "Display Mode",
68864
+ filter_value: option.label
68865
+ });
68866
+ },
68867
+ className: "flex w-full items-start px-4 py-2.5 text-left transition-colors hover:bg-slate-50",
68868
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full flex-col", children: [
68869
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
68870
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-sm ${isSelected ? "font-semibold text-blue-600" : "font-medium text-slate-700"}`, children: option.label }),
68871
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-3 inline-flex items-center gap-2", children: isSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4 text-blue-600" }) })
68872
+ ] }),
68873
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 text-xs text-slate-500", children: option.description })
68874
+ ] })
68875
+ },
68876
+ option.id
68877
+ );
68878
+ })
68879
+ }
68880
+ ) : null
68881
+ ] }), [displayMode, isDisplayModeMenuOpen, selectedLineIds, updateSelectedLineIds, visibleLineIds, worstPerformanceWorkspaceIds]);
68414
68882
  const gridToolbarControls = React148.useMemo(() => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-2", children: [
68415
68883
  /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: visibleAllGreenStreakDisplay ? /* @__PURE__ */ jsxRuntime.jsxs(
68416
68884
  motion.div,
@@ -68428,8 +68896,10 @@ function HomeView({
68428
68896
  },
68429
68897
  visibleAllGreenStreakDisplay.startedAt
68430
68898
  ) : null }),
68431
- lineSelectorComponent
68899
+ lineSelectorComponent,
68900
+ displayModeSelectorComponent
68432
68901
  ] }), [
68902
+ displayModeSelectorComponent,
68433
68903
  lineSelectorComponent,
68434
68904
  visibleAllGreenStreakDisplay
68435
68905
  ]);
@@ -68606,14 +69076,17 @@ function HomeView({
68606
69076
  children: React148__namespace.default.createElement(WorkspaceGrid, {
68607
69077
  workspaces: workspaceMetricsWithBreakState,
68608
69078
  blueComparisonWorkspaces: currentBlueComparisonWorkspaceMetrics || workspaceMetricsWithBreakState,
69079
+ worstPerformanceWorkspaceIds: activeWorstPerformanceWorkspaceIds,
68609
69080
  lineNames: mergedLineNames,
68610
- lineOrder: selectedLineIds,
69081
+ lineOrder: displayMode === "slideshow" ? slideshowLineIds : selectedLineIds,
69082
+ activeSlideshowLineId: displayMode === "slideshow" ? slideshowActiveLineId : null,
68611
69083
  factoryView: factoryViewId,
68612
69084
  legend: effectiveEfficiencyLegend,
68613
69085
  videoSources,
68614
69086
  videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
68615
69087
  videoStreamsLoading: currentVideoStreamsLoading,
68616
69088
  displayNames: metricsDisplayNames,
69089
+ displayMode,
68617
69090
  hasFlowBuffers,
68618
69091
  className: "h-full",
68619
69092
  toolbarRightContent: gridToolbarControls,
@@ -68640,14 +69113,17 @@ function HomeView({
68640
69113
  workspaces: [],
68641
69114
  // Show empty grid while loading
68642
69115
  blueComparisonWorkspaces: [],
69116
+ worstPerformanceWorkspaceIds: [],
68643
69117
  lineNames: mergedLineNames,
68644
- lineOrder: selectedLineIds,
69118
+ lineOrder: displayMode === "slideshow" ? slideshowLineIds : selectedLineIds,
69119
+ activeSlideshowLineId: displayMode === "slideshow" ? slideshowActiveLineId : null,
68645
69120
  factoryView: factoryViewId,
68646
69121
  legend: effectiveEfficiencyLegend,
68647
69122
  videoSources,
68648
69123
  videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
68649
69124
  videoStreamsLoading: currentVideoStreamsLoading,
68650
69125
  displayNames: metricsDisplayNames,
69126
+ displayMode,
68651
69127
  hasFlowBuffers,
68652
69128
  className: "h-full",
68653
69129
  toolbarRightContent: gridToolbarControls,
@@ -81088,9 +81564,11 @@ var WorkspaceDetailView = ({
81088
81564
  const prefetchLowMoments = async () => {
81089
81565
  try {
81090
81566
  const initData = await s3Service.getClipsInit(workspaceId, resolvedDate, resolvedShiftId, totalOutput);
81091
- const lowMomentsCount = Number(initData?.counts?.recent_flow_red_streak || 0);
81567
+ const hasLowMomentsCategory = Array.isArray(initData?.clipTypes) ? initData.clipTypes.some((type) => type?.type === "recent_flow_red_streak" || type?.id === "recent_flow_red_streak") : false;
81568
+ const lowMomentsCount = hasLowMomentsCategory ? Number(initData?.counts?.recent_flow_red_streak || 0) : 0;
81092
81569
  const lowMomentsKey = [
81093
81570
  "recent_flow_red_streak",
81571
+ workspaceId,
81094
81572
  resolvedDate,
81095
81573
  resolvedShiftId,
81096
81574
  initData?.snapshotDateTime ?? "nosnap",
@@ -81101,15 +81579,15 @@ var WorkspaceDetailView = ({
81101
81579
  return;
81102
81580
  }
81103
81581
  const initFirstVideo = initData?.firstClips?.recent_flow_red_streak ?? null;
81104
- if (lowMomentsCount <= 0) {
81105
- setLowMomentsPrefetch((prev) => prev?.key === lowMomentsKey ? {
81582
+ if (!hasLowMomentsCategory || lowMomentsCount <= 0) {
81583
+ setLowMomentsPrefetch({
81106
81584
  key: lowMomentsKey,
81107
81585
  metadata: [],
81108
81586
  firstVideo: null,
81109
81587
  total: 0,
81110
81588
  loading: false,
81111
81589
  error: null
81112
- } : prev);
81590
+ });
81113
81591
  prewarmedClipsRef.current.add(cacheKey);
81114
81592
  return;
81115
81593
  }