@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.mjs CHANGED
@@ -10,7 +10,7 @@ import { EventEmitter } from 'events';
10
10
  import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
11
11
  import Hls, { Events, ErrorTypes } from 'hls.js';
12
12
  import useSWR from 'swr';
13
- import { Camera, AlertTriangle, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, RefreshCw, Filter, X, Coffee, Plus, ArrowUp, ArrowDown, ArrowRight, HelpCircle, ClipboardX, Activity, Wrench, UserX, Clock, Package, CheckCircle2, ArrowLeft, Calendar, Save, AlertCircle, Loader2, Minus, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, XCircle, Palette, LockKeyhole, TrendingDown, FolderOpen, Folder, ArrowDownWideNarrow, Tag, Sliders, Layers, Search, Edit2, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Video, Copy, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, Film, MessageSquare, Menu, Send, Settings, LifeBuoy, EyeOff, Zap, ExternalLink, Flame, Crown, Medal } from 'lucide-react';
13
+ import { Camera, AlertTriangle, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, RefreshCw, Filter, X, Coffee, Plus, ArrowUp, ArrowDown, ArrowRight, HelpCircle, ClipboardX, Activity, Wrench, UserX, Clock, Package, Monitor, CheckCircle2, ArrowLeft, Calendar, Save, AlertCircle, Loader2, Minus, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, XCircle, Palette, LockKeyhole, TrendingDown, FolderOpen, Folder, ArrowDownWideNarrow, Tag, Sliders, Layers, Search, Edit2, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Video, Copy, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, Film, MessageSquare, Menu, Send, Settings, LifeBuoy, EyeOff, Zap, ExternalLink, Flame, Crown, Medal } from 'lucide-react';
14
14
  import { memo, noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds } from 'motion-utils';
15
15
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, ReferenceLine, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, Customized, Cell, PieChart, Pie, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
16
16
  import { Slot } from '@radix-ui/react-slot';
@@ -14871,6 +14871,7 @@ var transformMonitorWorkspaceMetrics = ({
14871
14871
  leaderboard_value: item.leaderboard_value ?? null,
14872
14872
  avg_recent_flow: item.avg_recent_flow ?? null,
14873
14873
  recent_flow_percent: item.recent_flow_percent ?? null,
14874
+ recent_flow_hourly: item.recent_flow_hourly ?? null,
14874
14875
  recent_flow_window_minutes: item.recent_flow_window_minutes ?? null,
14875
14876
  recent_flow_effective_end_at: item.recent_flow_effective_end_at ?? null,
14876
14877
  recent_flow_computed_at: item.recent_flow_computed_at ?? null,
@@ -38473,6 +38474,114 @@ var getRawVideoGridMetricValue = (workspace) => {
38473
38474
  };
38474
38475
  var hasIncomingWipMapping = (workspace) => Boolean(workspace.incoming_wip_buffer_name);
38475
38476
  var getVideoGridWorkspaceKey = (workspace) => workspace.workspace_uuid || `${workspace.line_id || "unknown"}:${workspace.workspace_name || "unknown"}`;
38477
+ var getWorstPerformanceWorkstationLimit = (totalWorkstations) => {
38478
+ if (!Number.isFinite(totalWorkstations) || totalWorkstations <= 0) return 0;
38479
+ if (totalWorkstations === 2) return 1;
38480
+ if (totalWorkstations === 4) return 2;
38481
+ return Math.min(3, Math.max(1, Math.floor(totalWorkstations)));
38482
+ };
38483
+ var coerceRecentFlowHourlyValue = (value) => {
38484
+ if (typeof value === "number" && Number.isFinite(value)) {
38485
+ return value;
38486
+ }
38487
+ if (typeof value === "string") {
38488
+ const trimmed = value.trim();
38489
+ if (!trimmed || trimmed.toLowerCase() === "x") return null;
38490
+ const parsed = Number(trimmed);
38491
+ return Number.isFinite(parsed) ? parsed : null;
38492
+ }
38493
+ return null;
38494
+ };
38495
+ var collectRecentFlowHourlyValues = (hourly) => {
38496
+ if (!hourly || typeof hourly !== "object" || Array.isArray(hourly)) {
38497
+ return [];
38498
+ }
38499
+ const values = [];
38500
+ Object.values(hourly).forEach((rawSlotValues) => {
38501
+ if (Array.isArray(rawSlotValues)) {
38502
+ rawSlotValues.forEach((rawValue) => {
38503
+ const value = coerceRecentFlowHourlyValue(rawValue);
38504
+ if (value !== null) values.push(value);
38505
+ });
38506
+ return;
38507
+ }
38508
+ if (rawSlotValues && typeof rawSlotValues === "object") {
38509
+ Object.values(rawSlotValues).forEach((rawValue) => {
38510
+ const value = coerceRecentFlowHourlyValue(rawValue);
38511
+ if (value !== null) values.push(value);
38512
+ });
38513
+ }
38514
+ });
38515
+ return values;
38516
+ };
38517
+ var getAverageRecentFlowHourlyPercent = (workspace) => {
38518
+ const values = collectRecentFlowHourlyValues(workspace.recent_flow_hourly);
38519
+ if (values.length > 0) {
38520
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
38521
+ }
38522
+ if (isFiniteNumber4(workspace.avg_recent_flow)) {
38523
+ return workspace.avg_recent_flow;
38524
+ }
38525
+ return isFiniteNumber4(workspace.recent_flow_percent) ? workspace.recent_flow_percent : null;
38526
+ };
38527
+ var getAssemblyCycleOverStandardRatio = (workspace) => {
38528
+ const medianCycleTime = toFiniteNumberOrNull(workspace.avg_cycle_time);
38529
+ const standardCycleTime = toFiniteNumberOrNull(workspace.ideal_cycle_time);
38530
+ if (medianCycleTime === null || standardCycleTime === null || medianCycleTime <= 0 || standardCycleTime <= 0) {
38531
+ return null;
38532
+ }
38533
+ return medianCycleTime / standardCycleTime;
38534
+ };
38535
+ var getUptimeWorstPerformanceValue = (workspace) => toFiniteNumberOrNull(workspace.efficiency);
38536
+ var isWorstPerformanceEligible = (workspace) => isValidAggregateEfficiency(workspace.monitoring_mode, workspace.efficiency);
38537
+ var isAssemblyLineGroup = (lineWorkspaces) => (
38538
+ // `assembly_enabled` is denormalized from `lines.assembly` onto every
38539
+ // live-monitor workspace row; it is not a workstation-level flag.
38540
+ lineWorkspaces.some((workspace) => workspace.assembly_enabled === true)
38541
+ );
38542
+ var getWorstPerformanceLineMode = (lineWorkspaces) => {
38543
+ if (lineWorkspaces.some((workspace) => normalizeMonitoringMode(workspace.monitoring_mode) === "uptime")) {
38544
+ return "uptime";
38545
+ }
38546
+ return isAssemblyLineGroup(lineWorkspaces) ? "assembly" : "flow";
38547
+ };
38548
+ var getEligibleWorstPerformanceCandidates = (lineWorkspaces) => lineWorkspaces.map((workspace, index) => ({ workspace, index })).filter(({ workspace }) => isWorstPerformanceEligible(workspace));
38549
+ var selectWorstPerformanceWorkspaceIds = (workspaces) => {
38550
+ const workspacesByLine = /* @__PURE__ */ new Map();
38551
+ workspaces.forEach((workspace) => {
38552
+ const lineKey = workspace.line_id || "unknown";
38553
+ const lineWorkspaces = workspacesByLine.get(lineKey) || [];
38554
+ lineWorkspaces.push(workspace);
38555
+ workspacesByLine.set(lineKey, lineWorkspaces);
38556
+ });
38557
+ const selected = /* @__PURE__ */ new Set();
38558
+ workspacesByLine.forEach((lineWorkspaces) => {
38559
+ const limit = getWorstPerformanceWorkstationLimit(lineWorkspaces.length);
38560
+ if (limit <= 0) return;
38561
+ const eligibleCandidates = getEligibleWorstPerformanceCandidates(lineWorkspaces);
38562
+ if (eligibleCandidates.length === 0) return;
38563
+ const lineMode = getWorstPerformanceLineMode(lineWorkspaces);
38564
+ if (lineMode === "uptime") {
38565
+ eligibleCandidates.map((candidate) => ({
38566
+ ...candidate,
38567
+ efficiency: getUptimeWorstPerformanceValue(candidate.workspace)
38568
+ })).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)));
38569
+ return;
38570
+ }
38571
+ if (lineMode === "assembly") {
38572
+ eligibleCandidates.map((candidate) => ({
38573
+ ...candidate,
38574
+ ratio: getAssemblyCycleOverStandardRatio(candidate.workspace)
38575
+ })).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)));
38576
+ return;
38577
+ }
38578
+ eligibleCandidates.map((candidate) => ({
38579
+ ...candidate,
38580
+ averageFlow: getAverageRecentFlowHourlyPercent(candidate.workspace)
38581
+ })).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)));
38582
+ });
38583
+ return selected;
38584
+ };
38476
38585
  var getVideoGridBlueComparisonGroupKey = (workspace) => {
38477
38586
  const factoryAreaId = typeof workspace.factory_area_id === "string" ? workspace.factory_area_id.trim() : "";
38478
38587
  if (factoryAreaId && workspace.factory_area_enabled === true) {
@@ -38765,6 +38874,7 @@ var VideoCard = React148__default.memo(({
38765
38874
  displayMinuteBucket,
38766
38875
  displayName,
38767
38876
  isBlueBest = false,
38877
+ isWorstPerformance = false,
38768
38878
  lastSeenLabel,
38769
38879
  hasRecentHealthSignal: hasRecentHealthSignal2 = false,
38770
38880
  onMouseEnter,
@@ -38803,6 +38913,8 @@ var VideoCard = React148__default.memo(({
38803
38913
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "blue" ? "bg-[#0EA5E9]/30" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
38804
38914
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "blue" ? "bg-[#0EA5E9]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
38805
38915
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "blue" ? "Best" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
38916
+ 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";
38917
+ 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";
38806
38918
  const trendInfo = workspace.trend !== void 0 ? getTrendArrowAndColor(workspace.trend) : null;
38807
38919
  const handleClick = useCallback(() => {
38808
38920
  trackCoreEvent("Workspace Card Clicked", {
@@ -38821,6 +38933,7 @@ var VideoCard = React148__default.memo(({
38821
38933
  {
38822
38934
  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}`,
38823
38935
  style: { width: "100%", height: "100%" },
38936
+ "data-worst-performance-highlight": isWorstPerformance ? "true" : void 0,
38824
38937
  onClick: handleClick,
38825
38938
  onMouseEnter,
38826
38939
  onMouseLeave,
@@ -38882,7 +38995,7 @@ var VideoCard = React148__default.memo(({
38882
38995
  "div",
38883
38996
  {
38884
38997
  "data-testid": "video-card-status-badges",
38885
- className: `absolute ${compact ? "top-1.5 left-1.5 gap-1" : "top-2 left-2 gap-1.5"} z-30 flex items-center`,
38998
+ className: `absolute ${statusBadgesPositionClass} z-30 flex items-center`,
38886
38999
  children: statusBadges.map((badge, index) => {
38887
39000
  const presentation = getIdleReasonPresentation({
38888
39001
  label: badge.label,
@@ -38933,6 +39046,17 @@ var VideoCard = React148__default.memo(({
38933
39046
  })
38934
39047
  }
38935
39048
  ),
39049
+ isWorstPerformance && /* @__PURE__ */ jsxs(
39050
+ "div",
39051
+ {
39052
+ "data-testid": "video-card-worst-performance-marker",
39053
+ 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}`,
39054
+ children: [
39055
+ /* @__PURE__ */ jsx(AlertTriangle, { className: `${compact ? "h-3.5 w-3.5" : "h-4 w-4"} text-[#E34329]`, "aria-hidden": "true" }),
39056
+ /* @__PURE__ */ jsx("span", { children: "Overall Underperformer" })
39057
+ ]
39058
+ }
39059
+ ),
38936
39060
  /* @__PURE__ */ jsx("div", { className: `absolute bottom-0 left-0 right-0 ${compact ? "h-0.5" : "h-1"} bg-black/50 z-30`, children: /* @__PURE__ */ jsx(
38937
39061
  "div",
38938
39062
  {
@@ -38981,6 +39105,9 @@ var VideoCard = React148__default.memo(({
38981
39105
  if (prevProps.isBlueBest !== nextProps.isBlueBest) {
38982
39106
  return false;
38983
39107
  }
39108
+ if (prevProps.isWorstPerformance !== nextProps.isWorstPerformance) {
39109
+ return false;
39110
+ }
38984
39111
  if (prevProps.lastSeenLabel !== nextProps.lastSeenLabel) {
38985
39112
  return false;
38986
39113
  }
@@ -39023,6 +39150,7 @@ var hasRecentHealthSignal = (lastHeartbeat) => {
39023
39150
  var VideoGridView = React148__default.memo(({
39024
39151
  workspaces,
39025
39152
  blueComparisonWorkspaces,
39153
+ worstPerformanceWorkspaceIds = [],
39026
39154
  selectedLine,
39027
39155
  className = "",
39028
39156
  legend,
@@ -39030,6 +39158,9 @@ var VideoGridView = React148__default.memo(({
39030
39158
  videoStreamsByWorkspaceId,
39031
39159
  videoStreamsLoading = false,
39032
39160
  displayNames = {},
39161
+ lineOrder = [],
39162
+ activeSlideshowLineId = null,
39163
+ displayMode = "all",
39033
39164
  onWorkspaceHover,
39034
39165
  onWorkspaceHoverEnd
39035
39166
  }) => {
@@ -39152,6 +39283,48 @@ var VideoGridView = React148__default.memo(({
39152
39283
  });
39153
39284
  }, [filteredWorkspaces]);
39154
39285
  const blueWinnerIds = useMemo(() => selectVideoGridBlueWinnerIds(blueComparisonWorkspaces || sortedWorkspaces, effectiveLegend), [blueComparisonWorkspaces, sortedWorkspaces, effectiveLegend]);
39286
+ const worstPerformanceIdSet = useMemo(
39287
+ () => new Set(worstPerformanceWorkspaceIds),
39288
+ [worstPerformanceWorkspaceIds]
39289
+ );
39290
+ const modeBaseWorkspaces = useMemo(() => {
39291
+ if (displayMode !== "red_only") {
39292
+ return sortedWorkspaces;
39293
+ }
39294
+ return sortedWorkspaces.filter((workspace) => getVideoGridColorState(workspace, effectiveLegend, blueWinnerIds) === "red");
39295
+ }, [blueWinnerIds, displayMode, effectiveLegend, sortedWorkspaces]);
39296
+ const slideshowLines = useMemo(() => {
39297
+ const linesWithWorkspaces = new Set(
39298
+ sortedWorkspaces.map((workspace) => workspace.line_id).filter((lineId) => Boolean(lineId))
39299
+ );
39300
+ if (lineOrder.length > 0) {
39301
+ return lineOrder.filter((lineId) => linesWithWorkspaces.has(lineId));
39302
+ }
39303
+ const orderedLineIds = [];
39304
+ const seenLineIds = /* @__PURE__ */ new Set();
39305
+ for (const workspace of sortedWorkspaces) {
39306
+ if (!workspace.line_id || seenLineIds.has(workspace.line_id)) {
39307
+ continue;
39308
+ }
39309
+ seenLineIds.add(workspace.line_id);
39310
+ orderedLineIds.push(workspace.line_id);
39311
+ }
39312
+ return orderedLineIds;
39313
+ }, [lineOrder, sortedWorkspaces]);
39314
+ const currentSlideshowLineId = displayMode === "slideshow" && slideshowLines.length > 0 ? activeSlideshowLineId && slideshowLines.includes(activeSlideshowLineId) ? activeSlideshowLineId : slideshowLines[0] : null;
39315
+ const displayWorkspaces = useMemo(() => {
39316
+ if (displayMode === "red_only") {
39317
+ return modeBaseWorkspaces;
39318
+ }
39319
+ if (displayMode !== "slideshow") {
39320
+ return sortedWorkspaces;
39321
+ }
39322
+ if (slideshowLines.length === 0) {
39323
+ return [];
39324
+ }
39325
+ const currentLineId = currentSlideshowLineId || slideshowLines[0];
39326
+ return sortedWorkspaces.filter((workspace) => workspace.line_id === currentLineId);
39327
+ }, [currentSlideshowLineId, displayMode, modeBaseWorkspaces, slideshowLines, sortedWorkspaces]);
39155
39328
  const streamsResolvedForWorkspaceSet = resolvedStreamWorkspaceKey === workspaceIdsKey;
39156
39329
  const resolveWorkspaceDisplayName = useCallback((workspace) => {
39157
39330
  return workspace.displayName || displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || workspace.workspace_name;
@@ -39162,7 +39335,7 @@ var VideoGridView = React148__default.memo(({
39162
39335
  const rawContainerWidth = containerRef.current.clientWidth;
39163
39336
  const containerWidth = rawContainerWidth - containerPadding;
39164
39337
  const containerHeight = containerRef.current.clientHeight - containerPadding;
39165
- const count = filteredWorkspaces.length;
39338
+ const count = displayWorkspaces.length;
39166
39339
  if (count === 0) {
39167
39340
  setGridCols(1);
39168
39341
  setGridRows(1);
@@ -39213,7 +39386,7 @@ var VideoGridView = React148__default.memo(({
39213
39386
  setGridCols(bestCols);
39214
39387
  setGridRows(rows);
39215
39388
  setIsMobileScrollableGrid(shouldUseMobileScroll);
39216
- }, [filteredWorkspaces.length, selectedLine]);
39389
+ }, [displayWorkspaces.length, selectedLine]);
39217
39390
  useEffect(() => {
39218
39391
  calculateOptimalGrid();
39219
39392
  const handleResize = () => calculateOptimalGrid();
@@ -39247,7 +39420,7 @@ var VideoGridView = React148__default.memo(({
39247
39420
  return () => {
39248
39421
  observerRef.current?.disconnect();
39249
39422
  };
39250
- }, [filteredWorkspaces]);
39423
+ }, [displayWorkspaces]);
39251
39424
  const prewarmClipsInit = useCallback((workspace) => {
39252
39425
  if (!dashboardConfig?.s3Config) return;
39253
39426
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
@@ -39333,7 +39506,7 @@ var VideoGridView = React148__default.memo(({
39333
39506
  });
39334
39507
  }, []);
39335
39508
  const workspaceCards = useMemo(() => {
39336
- return sortedWorkspaces.map((workspace) => {
39509
+ return displayWorkspaces.map((workspace) => {
39337
39510
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
39338
39511
  const workspaceKey = `${workspace.line_id || "unknown"}-${workspaceId}`;
39339
39512
  const isVisible = visibleWorkspaces.has(workspaceId);
@@ -39361,12 +39534,14 @@ var VideoGridView = React148__default.memo(({
39361
39534
  shouldPlay,
39362
39535
  lastSeenLabel,
39363
39536
  hasRecentHealthSignal: hasRecentHealthSignal(workspaceHealth?.lastHeartbeat),
39364
- isBlueBest: blueWinnerIds.has(getVideoGridWorkspaceKey(workspace))
39537
+ isBlueBest: blueWinnerIds.has(getVideoGridWorkspaceKey(workspace)),
39538
+ isWorstPerformance: worstPerformanceIdSet.has(getVideoGridWorkspaceKey(workspace))
39365
39539
  };
39366
39540
  });
39367
39541
  }, [
39368
- sortedWorkspaces,
39542
+ displayWorkspaces,
39369
39543
  blueWinnerIds,
39544
+ worstPerformanceIdSet,
39370
39545
  visibleWorkspaces,
39371
39546
  getWorkspaceCropping,
39372
39547
  videoStreamsByWorkspaceId,
@@ -39414,6 +39589,7 @@ var VideoGridView = React148__default.memo(({
39414
39589
  displayMinuteBucket,
39415
39590
  compact: !selectedLine,
39416
39591
  isBlueBest: card.isBlueBest,
39592
+ isWorstPerformance: card.isWorstPerformance,
39417
39593
  onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(card.workspaceId) : void 0,
39418
39594
  onMouseLeave: onWorkspaceHoverEnd ? () => onWorkspaceHoverEnd(card.workspaceId) : void 0
39419
39595
  }
@@ -39439,9 +39615,28 @@ var VideoGridView = React148__default.memo(({
39439
39615
  "data-testid": "video-grid-scroll-container",
39440
39616
  "data-mobile-scrollable": isMobileScrollableGrid ? "true" : "false",
39441
39617
  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"}`,
39442
- children: /* @__PURE__ */ jsx(
39443
- "div",
39618
+ children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "popLayout", children: displayMode === "red_only" && workspaceCards.length === 0 ? /* @__PURE__ */ jsxs(
39619
+ motion.div,
39444
39620
  {
39621
+ initial: { opacity: 0, scale: 0.95 },
39622
+ animate: { opacity: 1, scale: 1 },
39623
+ exit: { opacity: 0, scale: 0.95 },
39624
+ transition: { duration: 0.3 },
39625
+ "data-testid": "video-grid-empty-red-cameras",
39626
+ 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",
39627
+ children: [
39628
+ /* @__PURE__ */ jsx("div", { className: "text-base font-semibold text-emerald-900", children: "No red cameras right now" }),
39629
+ /* @__PURE__ */ 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." })
39630
+ ]
39631
+ },
39632
+ "empty-red"
39633
+ ) : /* @__PURE__ */ jsx(
39634
+ motion.div,
39635
+ {
39636
+ initial: displayMode === "slideshow" ? { opacity: 0, x: 100 } : { opacity: 0 },
39637
+ animate: displayMode === "slideshow" ? { opacity: 1, x: 0 } : { opacity: 1 },
39638
+ exit: displayMode === "slideshow" ? { opacity: 0, x: -100 } : { opacity: 0 },
39639
+ transition: { duration: 0.5, ease: [0.32, 0.72, 0, 1] },
39445
39640
  "data-testid": "video-grid-layout",
39446
39641
  className: `grid min-w-0 w-full gap-1.5 sm:gap-2 ${isMobileScrollableGrid ? "content-start" : "h-full"}`,
39447
39642
  style: {
@@ -39453,8 +39648,9 @@ var VideoGridView = React148__default.memo(({
39453
39648
  card,
39454
39649
  isMobileScrollableGrid ? "workspace-card relative min-w-0 w-full aspect-video min-h-[92px]" : "workspace-card relative min-w-0 w-full h-full"
39455
39650
  ))
39456
- }
39457
- )
39651
+ },
39652
+ `grid-${displayMode === "slideshow" ? currentSlideshowLineId : "standard"}`
39653
+ ) })
39458
39654
  }
39459
39655
  ) });
39460
39656
  });
@@ -47168,6 +47364,7 @@ function useClipsRealtimeUpdates({
47168
47364
  hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
47169
47365
  };
47170
47366
  }
47367
+ var LOW_EFFICIENCY_CATEGORY_ID = "recent_flow_red_streak";
47171
47368
  var parseFiniteNumber2 = (value) => {
47172
47369
  if (typeof value === "number" && Number.isFinite(value)) {
47173
47370
  return value;
@@ -47426,6 +47623,7 @@ var BottlenecksContent = ({
47426
47623
  totalOutput,
47427
47624
  enabled: isEffectiveShiftReady
47428
47625
  });
47626
+ const isLowMomentsCategoryAvailable = useMemo(() => Array.isArray(clipTypes) && clipTypes.some((type) => type?.type === LOW_EFFICIENCY_CATEGORY_ID || type?.id === LOW_EFFICIENCY_CATEGORY_ID), [clipTypes]);
47429
47627
  console.log("[BottlenecksContent] Clip types data:", {
47430
47628
  clipTypes,
47431
47629
  clipTypesLength: clipTypes?.length,
@@ -47476,20 +47674,20 @@ var BottlenecksContent = ({
47476
47674
  if (initialFilter) {
47477
47675
  return;
47478
47676
  }
47479
- const redStreakType = "recent_flow_red_streak";
47480
- if (dynamicCounts[redStreakType] > 0) {
47677
+ const redStreakType = LOW_EFFICIENCY_CATEGORY_ID;
47678
+ if (isLowMomentsCategoryAvailable && dynamicCounts[redStreakType] > 0) {
47481
47679
  setInitialFilter(redStreakType);
47482
47680
  setActiveFilter(redStreakType);
47483
47681
  activeFilterRef.current = redStreakType;
47484
47682
  return;
47485
47683
  }
47486
- if (defaultCategory) {
47684
+ if (defaultCategory && (defaultCategory !== LOW_EFFICIENCY_CATEGORY_ID || isLowMomentsCategoryAvailable)) {
47487
47685
  setInitialFilter(defaultCategory);
47488
47686
  setActiveFilter(defaultCategory);
47489
47687
  activeFilterRef.current = defaultCategory;
47490
47688
  return;
47491
47689
  }
47492
- }, [clipTypes, dynamicCounts, defaultCategory, initialFilter]);
47690
+ }, [clipTypes, dynamicCounts, defaultCategory, initialFilter, isLowMomentsCategoryAvailable]);
47493
47691
  const mergedCounts = useMemo(() => {
47494
47692
  return { ...dynamicCounts };
47495
47693
  }, [dynamicCounts]);
@@ -47708,7 +47906,7 @@ var BottlenecksContent = ({
47708
47906
  };
47709
47907
  const [cycleClips, redFlowClips, idleClips] = await Promise.all([
47710
47908
  fetchCategoryMetadata("cycle_completion"),
47711
- fetchCategoryMetadata("recent_flow_red_streak"),
47909
+ isLowMomentsCategoryAvailable ? fetchCategoryMetadata(LOW_EFFICIENCY_CATEGORY_ID) : Promise.resolve([]),
47712
47910
  fetchCategoryMetadata("idle_time")
47713
47911
  ]);
47714
47912
  const allClips = [...cycleClips, ...redFlowClips, ...idleClips].sort(
@@ -47722,7 +47920,7 @@ var BottlenecksContent = ({
47722
47920
  }
47723
47921
  };
47724
47922
  fetchTriageClips();
47725
- }, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, supabase, isEffectiveShiftReady]);
47923
+ }, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, supabase, isEffectiveShiftReady, isLowMomentsCategoryAvailable]);
47726
47924
  useEffect(() => {
47727
47925
  if (!idleTimeVlmEnabled || !triageMode || triageClips.length === 0) {
47728
47926
  return;
@@ -47760,7 +47958,7 @@ var BottlenecksContent = ({
47760
47958
  }, [idleTimeVlmEnabled, triageClips, triageMode, getAuthToken4]);
47761
47959
  useEffect(() => {
47762
47960
  if (s3ClipsService && (mergedCounts[activeFilter] || 0) > 0) {
47763
- if (activeFilter === "recent_flow_red_streak") {
47961
+ if (activeFilter === LOW_EFFICIENCY_CATEGORY_ID) {
47764
47962
  return;
47765
47963
  }
47766
47964
  if (firstClip && firstClip.type === activeFilter) {
@@ -47783,12 +47981,12 @@ var BottlenecksContent = ({
47783
47981
  return ["fast-cycles", "slow-cycles", "longest-idles"].includes(categoryId);
47784
47982
  }, [isFastSlowClipFiltersEnabled]);
47785
47983
  const shouldUseMetadataNavigation = useCallback((categoryId) => {
47786
- return isPercentileCategory(categoryId) || categoryId === "recent_flow_red_streak" || categoryId === "idle_time" && idleClipSort === "idle_duration_desc";
47984
+ return isPercentileCategory(categoryId) || categoryId === LOW_EFFICIENCY_CATEGORY_ID || categoryId === "idle_time" && idleClipSort === "idle_duration_desc";
47787
47985
  }, [idleClipSort, isPercentileCategory]);
47788
47986
  const getMetadataCacheKey = useCallback((categoryId) => {
47789
- const sortKey = categoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest";
47790
- return `${categoryId}-${effectiveDateString}-${effectiveShiftId}-${snapshotDateTime ?? "nosnap"}-${snapshotClipId ?? "nosnap"}-${sortKey}`;
47791
- }, [effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId, idleClipSort]);
47987
+ const sortKey = categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest";
47988
+ return `${categoryId}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-${snapshotDateTime ?? "nosnap"}-${snapshotClipId ?? "nosnap"}-${sortKey}`;
47989
+ }, [workspaceId, effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId, idleClipSort]);
47792
47990
  const setVisibleCategoryMetadata = useCallback((categoryId, clips) => {
47793
47991
  if (activeFilterRef.current !== categoryId) {
47794
47992
  return false;
@@ -47796,7 +47994,7 @@ var BottlenecksContent = ({
47796
47994
  categoryMetadataRef.current = clips;
47797
47995
  setCategoryMetadata(clips);
47798
47996
  setCategoryMetadataCategoryId(clips.length > 0 ? categoryId : null);
47799
- setCategoryMetadataSort(clips.length > 0 ? categoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest" : null);
47997
+ setCategoryMetadataSort(clips.length > 0 ? categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest" : null);
47800
47998
  return true;
47801
47999
  }, [idleClipSort]);
47802
48000
  const applyMetadataSnapshot = useCallback((categoryId, clips, total) => {
@@ -47849,7 +48047,8 @@ var BottlenecksContent = ({
47849
48047
  if (activeFilter !== "fast-cycles" && activeFilter !== "slow-cycles") {
47850
48048
  return;
47851
48049
  }
47852
- 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;
48050
+ const lowMomentsFallback = isLowMomentsCategoryAvailable ? LOW_EFFICIENCY_CATEGORY_ID : null;
48051
+ 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;
47853
48052
  if (!fallbackFilter || fallbackFilter === activeFilter) {
47854
48053
  return;
47855
48054
  }
@@ -47858,7 +48057,25 @@ var BottlenecksContent = ({
47858
48057
  setCategoryMetadataSort(null);
47859
48058
  categoryMetadataRef.current = [];
47860
48059
  updateActiveFilter(fallbackFilter);
47861
- }, [isFastSlowClipFiltersEnabled, activeFilter, dynamicCounts, clipTypes, updateActiveFilter]);
48060
+ }, [isFastSlowClipFiltersEnabled, activeFilter, dynamicCounts, clipTypes, updateActiveFilter, isLowMomentsCategoryAvailable]);
48061
+ useEffect(() => {
48062
+ if (activeFilter !== LOW_EFFICIENCY_CATEGORY_ID || isLowMomentsCategoryAvailable) {
48063
+ return;
48064
+ }
48065
+ 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 || "";
48066
+ setCategoryMetadata([]);
48067
+ setCategoryMetadataCategoryId(null);
48068
+ setCategoryMetadataSort(null);
48069
+ categoryMetadataRef.current = [];
48070
+ if (fallbackFilter) {
48071
+ setInitialFilter(fallbackFilter);
48072
+ updateActiveFilter(fallbackFilter);
48073
+ } else {
48074
+ setInitialFilter("");
48075
+ updateActiveFilter("");
48076
+ setIsCategoryLoading(false);
48077
+ }
48078
+ }, [activeFilter, isLowMomentsCategoryAvailable, clipTypes, dynamicCounts, updateActiveFilter]);
47862
48079
  useCallback((categoryId) => {
47863
48080
  if (isPercentileCategory(categoryId)) {
47864
48081
  return categoryMetadata.length;
@@ -47910,6 +48127,16 @@ var BottlenecksContent = ({
47910
48127
  }
47911
48128
  return;
47912
48129
  }
48130
+ if (categoryId === LOW_EFFICIENCY_CATEGORY_ID && !isLowMomentsCategoryAvailable) {
48131
+ setCategoryMetadata([]);
48132
+ setCategoryMetadataCategoryId(null);
48133
+ setCategoryMetadataSort(null);
48134
+ categoryMetadataRef.current = [];
48135
+ if (activeFilterRef.current === categoryId) {
48136
+ setIsCategoryLoading(false);
48137
+ }
48138
+ return;
48139
+ }
47913
48140
  if (!isEffectiveShiftReady) {
47914
48141
  console.log("[BottlenecksContent] Skipping metadata load - shift/date not ready");
47915
48142
  if (activeFilterRef.current === categoryId) {
@@ -47921,10 +48148,10 @@ var BottlenecksContent = ({
47921
48148
  const cacheKey = getMetadataCacheKey(categoryId);
47922
48149
  const candidateLowMomentsPrefetch = lowMomentsPrefetch ?? null;
47923
48150
  const lowMomentsPrefetchMatchesScope = Boolean(
47924
- candidateLowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`)
48151
+ candidateLowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`)
47925
48152
  );
47926
- 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;
47927
- const lowMomentsPrefetchInFlight = !forceRefresh && categoryId === "recent_flow_red_streak" && candidateLowMomentsPrefetch && (candidateLowMomentsPrefetch.key === cacheKey || lowMomentsPrefetchMatchesScope) && candidateLowMomentsPrefetch.loading;
48153
+ 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;
48154
+ const lowMomentsPrefetchInFlight = !forceRefresh && categoryId === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && candidateLowMomentsPrefetch && (candidateLowMomentsPrefetch.key === cacheKey || lowMomentsPrefetchMatchesScope) && candidateLowMomentsPrefetch.loading;
47928
48155
  if (lowMomentsPrefetchInFlight) {
47929
48156
  if (autoLoadFirstVideo && candidateLowMomentsPrefetch?.firstVideo) {
47930
48157
  applyPrefetchedFirstVideo(candidateLowMomentsPrefetch.firstVideo);
@@ -48015,7 +48242,7 @@ var BottlenecksContent = ({
48015
48242
  knownTotal: mergedCounts[categoryId] ?? null,
48016
48243
  snapshotDateTime,
48017
48244
  snapshotClipId,
48018
- sort: categoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
48245
+ sort: categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
48019
48246
  }),
48020
48247
  redirectReason: "session_expired"
48021
48248
  });
@@ -48109,19 +48336,19 @@ var BottlenecksContent = ({
48109
48336
  setIsCategoryLoading(false);
48110
48337
  }
48111
48338
  }
48112
- }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, idleClipSort, supabase, setVisibleCategoryMetadata, lowMomentsPrefetch, applyPrefetchedFirstVideo, applyMetadataSnapshot]);
48339
+ }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, idleClipSort, supabase, setVisibleCategoryMetadata, lowMomentsPrefetch, applyPrefetchedFirstVideo, applyMetadataSnapshot, isLowMomentsCategoryAvailable]);
48113
48340
  useEffect(() => {
48114
- if (activeFilter !== "recent_flow_red_streak") {
48341
+ if (activeFilter !== LOW_EFFICIENCY_CATEGORY_ID || !isLowMomentsCategoryAvailable) {
48115
48342
  return;
48116
48343
  }
48117
- if (!lowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`)) {
48344
+ if (!lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`)) {
48118
48345
  return;
48119
48346
  }
48120
- if (lowMomentsPrefetch.firstVideo && !allVideos.some((video) => video.type === "recent_flow_red_streak")) {
48347
+ if (lowMomentsPrefetch.firstVideo && !allVideos.some((video) => video.type === LOW_EFFICIENCY_CATEGORY_ID)) {
48121
48348
  applyPrefetchedFirstVideo(lowMomentsPrefetch.firstVideo);
48122
48349
  }
48123
48350
  if (lowMomentsPrefetch.metadata.length > 0) {
48124
- applyMetadataSnapshot("recent_flow_red_streak", lowMomentsPrefetch.metadata, lowMomentsPrefetch.total);
48351
+ applyMetadataSnapshot(LOW_EFFICIENCY_CATEGORY_ID, lowMomentsPrefetch.metadata, lowMomentsPrefetch.total);
48125
48352
  if (isMountedRef.current) {
48126
48353
  setIsCategoryLoading(false);
48127
48354
  }
@@ -48133,11 +48360,13 @@ var BottlenecksContent = ({
48133
48360
  }, [
48134
48361
  activeFilter,
48135
48362
  lowMomentsPrefetch,
48363
+ workspaceId,
48136
48364
  effectiveDateString,
48137
48365
  effectiveShiftId,
48138
48366
  allVideos,
48139
48367
  applyPrefetchedFirstVideo,
48140
- applyMetadataSnapshot
48368
+ applyMetadataSnapshot,
48369
+ isLowMomentsCategoryAvailable
48141
48370
  ]);
48142
48371
  useEffect(() => {
48143
48372
  if (previousIdleClipSortRef.current === idleClipSort) {
@@ -48833,11 +49062,11 @@ var BottlenecksContent = ({
48833
49062
  }
48834
49063
  return currentPosition;
48835
49064
  }, [activeFilter, categoryMetadata.length, currentMetadataIndex, currentPosition, shouldUseMetadataNavigation]);
48836
- const prefetchedExplorerMetadata = 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(
49065
+ const prefetchedExplorerMetadata = 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(
48837
49066
  activeFilter,
48838
49067
  categoryMetadataCategoryId,
48839
49068
  categoryMetadata
48840
- ), [activeFilter, categoryMetadata, categoryMetadataCategoryId, categoryMetadataSort, idleClipSort, lowMomentsPrefetch, effectiveDateString, effectiveShiftId]);
49069
+ ), [activeFilter, categoryMetadata, categoryMetadataCategoryId, categoryMetadataSort, idleClipSort, lowMomentsPrefetch, workspaceId, effectiveDateString, effectiveShiftId, isLowMomentsCategoryAvailable]);
48841
49070
  const classificationClipIds = useMemo(() => {
48842
49071
  if (!idleTimeVlmEnabled) {
48843
49072
  return [];
@@ -49610,7 +49839,7 @@ var BottlenecksContent = ({
49610
49839
  prefetchedClipMetadata: prefetchedExplorerMetadata,
49611
49840
  externallyManagedLoadingCategories: {
49612
49841
  recent_flow_red_streak: Boolean(
49613
- activeFilter === "recent_flow_red_streak" && lowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`) && lowMomentsPrefetch.loading
49842
+ activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && lowMomentsPrefetch.loading
49614
49843
  )
49615
49844
  },
49616
49845
  activeCategoryLoading: isCategoryLoading,
@@ -56883,10 +57112,12 @@ WorkspaceGridItem.displayName = "WorkspaceGridItem";
56883
57112
  var WorkspaceGrid = React148__default.memo(({
56884
57113
  workspaces,
56885
57114
  blueComparisonWorkspaces,
57115
+ worstPerformanceWorkspaceIds = [],
56886
57116
  isPdfMode = false,
56887
57117
  customWorkspacePositions,
56888
57118
  lineNames = {},
56889
57119
  lineOrder = [],
57120
+ activeSlideshowLineId = null,
56890
57121
  factoryView = "factory",
56891
57122
  line2Uuid = "line-2",
56892
57123
  className = "",
@@ -56897,7 +57128,8 @@ var WorkspaceGrid = React148__default.memo(({
56897
57128
  displayNames = {},
56898
57129
  onWorkspaceHover,
56899
57130
  onWorkspaceHoverEnd,
56900
- toolbarRightContent
57131
+ toolbarRightContent,
57132
+ displayMode = "all"
56901
57133
  }) => {
56902
57134
  const mapViewEnabled = false;
56903
57135
  const [viewMode, setViewMode] = useState(() => {
@@ -56955,13 +57187,16 @@ var WorkspaceGrid = React148__default.memo(({
56955
57187
  {
56956
57188
  workspaces,
56957
57189
  blueComparisonWorkspaces,
57190
+ worstPerformanceWorkspaceIds,
56958
57191
  lineNames,
56959
57192
  lineOrder,
57193
+ activeSlideshowLineId,
56960
57194
  videoSources,
56961
57195
  videoStreamsByWorkspaceId,
56962
57196
  videoStreamsLoading,
56963
57197
  displayNames,
56964
57198
  legend,
57199
+ displayMode,
56965
57200
  onWorkspaceHover,
56966
57201
  onWorkspaceHoverEnd
56967
57202
  }
@@ -67100,6 +67335,31 @@ var NotificationService = class {
67100
67335
  function createNotificationService(supabaseClient) {
67101
67336
  return new NotificationService(supabaseClient);
67102
67337
  }
67338
+
67339
+ // src/components/dashboard/grid/displayModes.ts
67340
+ var HOME_DISPLAY_MODE_OPTIONS = [
67341
+ {
67342
+ id: "all",
67343
+ label: "Default",
67344
+ description: "Show every camera in the selected line filter."
67345
+ },
67346
+ {
67347
+ id: "red_only",
67348
+ label: "Only red",
67349
+ description: "Only show workstations in red."
67350
+ },
67351
+ {
67352
+ id: "slideshow",
67353
+ label: "Slideshow",
67354
+ description: "Automatically switches between all lines every 15 seconds."
67355
+ },
67356
+ {
67357
+ id: "worst_workstations",
67358
+ label: "Overall Underperformers",
67359
+ description: "Lowest performers based on full day efficiency"
67360
+ }
67361
+ ];
67362
+ var getHomeDisplayModeLabel = (displayMode) => HOME_DISPLAY_MODE_OPTIONS.find((option) => option.id === displayMode)?.label || "Default";
67103
67363
  var KPISection2 = KPISection;
67104
67364
  var DEBUG_DASHBOARD_LOGS3 = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
67105
67365
  var logDebug3 = (...args) => {
@@ -67110,6 +67370,9 @@ var EMPTY_LINE_IDS = [];
67110
67370
  var EMPTY_WORKSPACES = [];
67111
67371
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
67112
67372
  var ALL_GREEN_MILESTONE_DURATION_MS = 6e3;
67373
+ var SLIDESHOW_ROTATION_INTERVAL_MS = 15e3;
67374
+ var SLIDESHOW_IDLE_REQUIRED_MS = 15e3;
67375
+ var SLIDESHOW_ACTIVE_LINE_STORAGE_KEY = "optifye_home_slideshow_active_line_id";
67113
67376
  var ALL_GREEN_CELEBRATION_SEEN_PREFIX = "optifye:all-green-celebration:v1:";
67114
67377
  var ALL_GREEN_MILESTONE_SEEN_PREFIX = "optifye:all-green-milestone:v1:";
67115
67378
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
@@ -67239,6 +67502,31 @@ function HomeView({
67239
67502
  const [isLineSelectorOpen, setIsLineSelectorOpen] = useState(false);
67240
67503
  const [pendingSelectedLineIds, setPendingSelectedLineIds] = useState([]);
67241
67504
  const lineSelectorRef = useRef(null);
67505
+ const [displayMode, setDisplayMode] = useState(() => {
67506
+ if (typeof window === "undefined") {
67507
+ return "all";
67508
+ }
67509
+ try {
67510
+ const savedDisplayMode = sessionStorage.getItem("optifye_home_display_mode");
67511
+ return HOME_DISPLAY_MODE_OPTIONS.some((option) => option.id === savedDisplayMode) ? savedDisplayMode : "all";
67512
+ } catch {
67513
+ return "all";
67514
+ }
67515
+ });
67516
+ const [isDisplayModeMenuOpen, setIsDisplayModeMenuOpen] = useState(false);
67517
+ const displayModeSelectorRef = useRef(null);
67518
+ const readPersistedSlideshowActiveLineId = () => {
67519
+ if (typeof window === "undefined") {
67520
+ return null;
67521
+ }
67522
+ try {
67523
+ return sessionStorage.getItem(SLIDESHOW_ACTIVE_LINE_STORAGE_KEY);
67524
+ } catch {
67525
+ return null;
67526
+ }
67527
+ };
67528
+ const [slideshowActiveLineId, setSlideshowActiveLineId] = useState(() => readPersistedSlideshowActiveLineId());
67529
+ const slideshowLastUserActivityAtRef = useRef(Date.now());
67242
67530
  useEffect(() => {
67243
67531
  if (isLineSelectorOpen) {
67244
67532
  setPendingSelectedLineIds(selectedLineIds);
@@ -67265,6 +67553,13 @@ function HomeView({
67265
67553
  console.warn("Failed to save line filter to sessionStorage:", error);
67266
67554
  }
67267
67555
  }, [selectedLineIds]);
67556
+ useEffect(() => {
67557
+ try {
67558
+ sessionStorage.setItem("optifye_home_display_mode", displayMode);
67559
+ } catch (error) {
67560
+ console.warn("Failed to save display mode to sessionStorage:", error);
67561
+ }
67562
+ }, [displayMode]);
67268
67563
  useEffect(() => {
67269
67564
  if (!isLineSelectorOpen) {
67270
67565
  return;
@@ -67279,6 +67574,20 @@ function HomeView({
67279
67574
  document.removeEventListener("mousedown", handleClickOutside);
67280
67575
  };
67281
67576
  }, [isLineSelectorOpen]);
67577
+ useEffect(() => {
67578
+ if (!isDisplayModeMenuOpen) {
67579
+ return;
67580
+ }
67581
+ const handleClickOutside = (event) => {
67582
+ if (displayModeSelectorRef.current && !displayModeSelectorRef.current.contains(event.target)) {
67583
+ setIsDisplayModeMenuOpen(false);
67584
+ }
67585
+ };
67586
+ document.addEventListener("mousedown", handleClickOutside);
67587
+ return () => {
67588
+ document.removeEventListener("mousedown", handleClickOutside);
67589
+ };
67590
+ }, [isDisplayModeMenuOpen]);
67282
67591
  const primarySelectedLineId = selectedLineIds[0] || defaultHomeLineId;
67283
67592
  const isMultiLineSelection = selectedLineIds.length > 1;
67284
67593
  const selectedLineIdsKey = selectedLineIds.join(",");
@@ -67581,6 +67890,88 @@ function HomeView({
67581
67890
  })),
67582
67891
  [currentWorkspaceMetrics, activeBreakLineIds]
67583
67892
  );
67893
+ const slideshowLineIds = useMemo(() => {
67894
+ const selectedLineIdSetForSlideshow = new Set(selectedLineIds);
67895
+ const linesWithVisibleWorkspaces = new Set(
67896
+ workspaceMetricsWithBreakState.map((workspace) => workspace.line_id).filter((lineId) => Boolean(lineId))
67897
+ );
67898
+ const selectedVisibleLines = visibleLineIds.filter((lineId) => selectedLineIdSetForSlideshow.has(lineId));
67899
+ const selectedLinesWithWorkspaces = selectedVisibleLines.filter((lineId) => linesWithVisibleWorkspaces.has(lineId));
67900
+ if (selectedLinesWithWorkspaces.length > 0) {
67901
+ return selectedLinesWithWorkspaces;
67902
+ }
67903
+ return selectedVisibleLines;
67904
+ }, [selectedLineIds, visibleLineIds, workspaceMetricsWithBreakState]);
67905
+ const slideshowLineIdsKey = slideshowLineIds.join(",");
67906
+ useEffect(() => {
67907
+ if (displayMode !== "slideshow") {
67908
+ return;
67909
+ }
67910
+ slideshowLastUserActivityAtRef.current = Date.now();
67911
+ setSlideshowActiveLineId((previousLineId) => {
67912
+ if (previousLineId && slideshowLineIds.includes(previousLineId)) {
67913
+ return previousLineId;
67914
+ }
67915
+ const persistedLineId = readPersistedSlideshowActiveLineId();
67916
+ if (persistedLineId && slideshowLineIds.includes(persistedLineId)) {
67917
+ return persistedLineId;
67918
+ }
67919
+ if (primarySelectedLineId && slideshowLineIds.includes(primarySelectedLineId)) {
67920
+ return primarySelectedLineId;
67921
+ }
67922
+ return slideshowLineIds[0] || null;
67923
+ });
67924
+ }, [displayMode, primarySelectedLineId, slideshowLineIdsKey]);
67925
+ useEffect(() => {
67926
+ if (typeof window === "undefined") {
67927
+ return;
67928
+ }
67929
+ try {
67930
+ if (slideshowActiveLineId) {
67931
+ sessionStorage.setItem(SLIDESHOW_ACTIVE_LINE_STORAGE_KEY, slideshowActiveLineId);
67932
+ } else {
67933
+ sessionStorage.removeItem(SLIDESHOW_ACTIVE_LINE_STORAGE_KEY);
67934
+ }
67935
+ } catch {
67936
+ }
67937
+ }, [slideshowActiveLineId]);
67938
+ useEffect(() => {
67939
+ if (displayMode !== "slideshow") {
67940
+ return void 0;
67941
+ }
67942
+ const markActivity = () => {
67943
+ slideshowLastUserActivityAtRef.current = Date.now();
67944
+ };
67945
+ const eventOptions = { passive: true };
67946
+ const activityEvents = ["mousemove", "mousedown", "keydown", "touchstart", "wheel"];
67947
+ activityEvents.forEach((eventName) => {
67948
+ window.addEventListener(eventName, markActivity, eventOptions);
67949
+ });
67950
+ return () => {
67951
+ activityEvents.forEach((eventName) => {
67952
+ window.removeEventListener(eventName, markActivity, eventOptions);
67953
+ });
67954
+ };
67955
+ }, [displayMode]);
67956
+ useEffect(() => {
67957
+ if (displayMode !== "slideshow" || slideshowLineIds.length <= 1) {
67958
+ return void 0;
67959
+ }
67960
+ const intervalId = window.setInterval(() => {
67961
+ const nowMs2 = Date.now();
67962
+ if (nowMs2 - slideshowLastUserActivityAtRef.current < SLIDESHOW_IDLE_REQUIRED_MS) {
67963
+ return;
67964
+ }
67965
+ setSlideshowActiveLineId((previousLineId) => {
67966
+ const previousIndex = previousLineId ? slideshowLineIds.indexOf(previousLineId) : -1;
67967
+ const nextIndex = previousIndex >= 0 ? (previousIndex + 1) % slideshowLineIds.length : 0;
67968
+ return slideshowLineIds[nextIndex] || null;
67969
+ });
67970
+ }, SLIDESHOW_ROTATION_INTERVAL_MS);
67971
+ return () => {
67972
+ window.clearInterval(intervalId);
67973
+ };
67974
+ }, [displayMode, slideshowLineIdsKey, slideshowLineIds]);
67584
67975
  const [breakNotificationsDismissed, setBreakNotificationsDismissed] = useState(false);
67585
67976
  useEffect(() => {
67586
67977
  if (currentActiveBreaks.length > 0) {
@@ -67592,6 +67983,11 @@ function HomeView({
67592
67983
  () => workspaceMetricsWithBreakState.filter((workspace) => Boolean(workspace.workspace_uuid || workspace.workspace_name)).length,
67593
67984
  [workspaceMetricsWithBreakState]
67594
67985
  );
67986
+ const worstPerformanceWorkspaceIds = useMemo(
67987
+ () => Array.from(selectWorstPerformanceWorkspaceIds(workspaceMetricsWithBreakState)),
67988
+ [workspaceMetricsWithBreakState]
67989
+ );
67990
+ const activeWorstPerformanceWorkspaceIds = displayMode === "worst_workstations" ? worstPerformanceWorkspaceIds : EMPTY_LINE_IDS;
67595
67991
  const allGreenCelebrationSignature = useMemo(() => {
67596
67992
  const workspaceSignature = workspaceMetricsWithBreakState.map((workspace) => workspace.workspace_uuid || `${workspace.line_id}:${workspace.workspace_name}`).filter(Boolean).sort().join(",");
67597
67993
  return `${selectedLineIds.join(",")}::${workspaceSignature}`;
@@ -68228,6 +68624,10 @@ function HomeView({
68228
68624
  selection_mode: isAllLinesSelection(normalizedLineIds) ? "all" : normalizedLineIds.length === 1 ? "single" : "custom",
68229
68625
  line_name: getLineSelectionLabel(normalizedLineIds)
68230
68626
  });
68627
+ trackCoreEvent("Dashboard Filter Selected", {
68628
+ filter_type: "Line Filter",
68629
+ filter_value: getLineSelectionLabel(normalizedLineIds)
68630
+ });
68231
68631
  }, [factoryViewId, getLineSelectionLabel, getTrackedLineScope, selectedLineIds, selectedLineIdsKey, visibleLineIds]);
68232
68632
  useCallback(() => {
68233
68633
  updateSelectedLineIds(visibleLineIds);
@@ -68282,7 +68682,7 @@ function HomeView({
68282
68682
  "aria-expanded": isLineSelectorOpen,
68283
68683
  "aria-label": "Select lines",
68284
68684
  children: [
68285
- /* @__PURE__ */ jsx("span", { className: "truncate", children: getLineSelectionLabel(selectedLineIds) }),
68685
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: displayMode === "slideshow" && slideshowActiveLineId ? getLineSelectionLabel([slideshowActiveLineId]) : getLineSelectionLabel(selectedLineIds) }),
68286
68686
  /* @__PURE__ */ jsx(ChevronDown, { className: `h-4 w-4 text-slate-400 transition-transform ${isLineSelectorOpen ? "rotate-180" : ""}` })
68287
68687
  ]
68288
68688
  }
@@ -68378,10 +68778,78 @@ function HomeView({
68378
68778
  selectedLineIds,
68379
68779
  pendingSelectedLineIds,
68380
68780
  lineSelectorSignalStatusByLine,
68781
+ displayMode,
68782
+ slideshowActiveLineId,
68381
68783
  visibleLineIds,
68382
68784
  updateSelectedLineIds,
68383
68785
  isAllLinesSelection
68384
68786
  ]);
68787
+ const displayModeSelectorComponent = useMemo(() => /* @__PURE__ */ jsxs("div", { ref: displayModeSelectorRef, className: "relative", children: [
68788
+ /* @__PURE__ */ jsx(
68789
+ "button",
68790
+ {
68791
+ type: "button",
68792
+ onClick: () => {
68793
+ setIsLineSelectorOpen(false);
68794
+ setIsDisplayModeMenuOpen((previous) => !previous);
68795
+ },
68796
+ 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"}`,
68797
+ "aria-haspopup": "menu",
68798
+ "aria-expanded": isDisplayModeMenuOpen,
68799
+ "aria-label": `Display mode: ${getHomeDisplayModeLabel(displayMode)}`,
68800
+ title: `Display mode: ${getHomeDisplayModeLabel(displayMode)}`,
68801
+ children: /* @__PURE__ */ jsx(Monitor, { className: "h-4 w-4" })
68802
+ }
68803
+ ),
68804
+ isDisplayModeMenuOpen ? /* @__PURE__ */ jsx(
68805
+ "div",
68806
+ {
68807
+ role: "menu",
68808
+ "aria-label": "Display modes",
68809
+ 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",
68810
+ children: HOME_DISPLAY_MODE_OPTIONS.map((option) => {
68811
+ const isSelected = option.id === displayMode;
68812
+ return /* @__PURE__ */ jsx(
68813
+ "button",
68814
+ {
68815
+ type: "button",
68816
+ role: "menuitemradio",
68817
+ "aria-checked": isSelected,
68818
+ onClick: () => {
68819
+ const nextSelectedLineIds = option.id === "slideshow" ? visibleLineIds : selectedLineIds;
68820
+ setDisplayMode(option.id);
68821
+ if (option.id === "slideshow") {
68822
+ updateSelectedLineIds(visibleLineIds);
68823
+ }
68824
+ setIsDisplayModeMenuOpen(false);
68825
+ trackCoreEvent("Monitor Display Filter Selected", {
68826
+ filter_name: option.label,
68827
+ filter_id: option.id,
68828
+ previous_display_mode: displayMode,
68829
+ selected_line_ids: nextSelectedLineIds,
68830
+ selected_line_count: nextSelectedLineIds.length,
68831
+ highlighted_workspace_count: option.id === "worst_workstations" ? worstPerformanceWorkspaceIds.length : 0
68832
+ });
68833
+ trackCoreEvent("Dashboard Filter Selected", {
68834
+ filter_type: "Display Mode",
68835
+ filter_value: option.label
68836
+ });
68837
+ },
68838
+ className: "flex w-full items-start px-4 py-2.5 text-left transition-colors hover:bg-slate-50",
68839
+ children: /* @__PURE__ */ jsxs("div", { className: "flex w-full flex-col", children: [
68840
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
68841
+ /* @__PURE__ */ jsx("span", { className: `text-sm ${isSelected ? "font-semibold text-blue-600" : "font-medium text-slate-700"}`, children: option.label }),
68842
+ /* @__PURE__ */ jsx("span", { className: "ml-3 inline-flex items-center gap-2", children: isSelected && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 text-blue-600" }) })
68843
+ ] }),
68844
+ /* @__PURE__ */ jsx("span", { className: "mt-1 text-xs text-slate-500", children: option.description })
68845
+ ] })
68846
+ },
68847
+ option.id
68848
+ );
68849
+ })
68850
+ }
68851
+ ) : null
68852
+ ] }), [displayMode, isDisplayModeMenuOpen, selectedLineIds, updateSelectedLineIds, visibleLineIds, worstPerformanceWorkspaceIds]);
68385
68853
  const gridToolbarControls = useMemo(() => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
68386
68854
  /* @__PURE__ */ jsx(AnimatePresence, { children: visibleAllGreenStreakDisplay ? /* @__PURE__ */ jsxs(
68387
68855
  motion.div,
@@ -68399,8 +68867,10 @@ function HomeView({
68399
68867
  },
68400
68868
  visibleAllGreenStreakDisplay.startedAt
68401
68869
  ) : null }),
68402
- lineSelectorComponent
68870
+ lineSelectorComponent,
68871
+ displayModeSelectorComponent
68403
68872
  ] }), [
68873
+ displayModeSelectorComponent,
68404
68874
  lineSelectorComponent,
68405
68875
  visibleAllGreenStreakDisplay
68406
68876
  ]);
@@ -68577,14 +69047,17 @@ function HomeView({
68577
69047
  children: React148__default.createElement(WorkspaceGrid, {
68578
69048
  workspaces: workspaceMetricsWithBreakState,
68579
69049
  blueComparisonWorkspaces: currentBlueComparisonWorkspaceMetrics || workspaceMetricsWithBreakState,
69050
+ worstPerformanceWorkspaceIds: activeWorstPerformanceWorkspaceIds,
68580
69051
  lineNames: mergedLineNames,
68581
- lineOrder: selectedLineIds,
69052
+ lineOrder: displayMode === "slideshow" ? slideshowLineIds : selectedLineIds,
69053
+ activeSlideshowLineId: displayMode === "slideshow" ? slideshowActiveLineId : null,
68582
69054
  factoryView: factoryViewId,
68583
69055
  legend: effectiveEfficiencyLegend,
68584
69056
  videoSources,
68585
69057
  videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
68586
69058
  videoStreamsLoading: currentVideoStreamsLoading,
68587
69059
  displayNames: metricsDisplayNames,
69060
+ displayMode,
68588
69061
  hasFlowBuffers,
68589
69062
  className: "h-full",
68590
69063
  toolbarRightContent: gridToolbarControls,
@@ -68611,14 +69084,17 @@ function HomeView({
68611
69084
  workspaces: [],
68612
69085
  // Show empty grid while loading
68613
69086
  blueComparisonWorkspaces: [],
69087
+ worstPerformanceWorkspaceIds: [],
68614
69088
  lineNames: mergedLineNames,
68615
- lineOrder: selectedLineIds,
69089
+ lineOrder: displayMode === "slideshow" ? slideshowLineIds : selectedLineIds,
69090
+ activeSlideshowLineId: displayMode === "slideshow" ? slideshowActiveLineId : null,
68616
69091
  factoryView: factoryViewId,
68617
69092
  legend: effectiveEfficiencyLegend,
68618
69093
  videoSources,
68619
69094
  videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
68620
69095
  videoStreamsLoading: currentVideoStreamsLoading,
68621
69096
  displayNames: metricsDisplayNames,
69097
+ displayMode,
68622
69098
  hasFlowBuffers,
68623
69099
  className: "h-full",
68624
69100
  toolbarRightContent: gridToolbarControls,
@@ -81059,9 +81535,11 @@ var WorkspaceDetailView = ({
81059
81535
  const prefetchLowMoments = async () => {
81060
81536
  try {
81061
81537
  const initData = await s3Service.getClipsInit(workspaceId, resolvedDate, resolvedShiftId, totalOutput);
81062
- const lowMomentsCount = Number(initData?.counts?.recent_flow_red_streak || 0);
81538
+ const hasLowMomentsCategory = Array.isArray(initData?.clipTypes) ? initData.clipTypes.some((type) => type?.type === "recent_flow_red_streak" || type?.id === "recent_flow_red_streak") : false;
81539
+ const lowMomentsCount = hasLowMomentsCategory ? Number(initData?.counts?.recent_flow_red_streak || 0) : 0;
81063
81540
  const lowMomentsKey = [
81064
81541
  "recent_flow_red_streak",
81542
+ workspaceId,
81065
81543
  resolvedDate,
81066
81544
  resolvedShiftId,
81067
81545
  initData?.snapshotDateTime ?? "nosnap",
@@ -81072,15 +81550,15 @@ var WorkspaceDetailView = ({
81072
81550
  return;
81073
81551
  }
81074
81552
  const initFirstVideo = initData?.firstClips?.recent_flow_red_streak ?? null;
81075
- if (lowMomentsCount <= 0) {
81076
- setLowMomentsPrefetch((prev) => prev?.key === lowMomentsKey ? {
81553
+ if (!hasLowMomentsCategory || lowMomentsCount <= 0) {
81554
+ setLowMomentsPrefetch({
81077
81555
  key: lowMomentsKey,
81078
81556
  metadata: [],
81079
81557
  firstVideo: null,
81080
81558
  total: 0,
81081
81559
  loading: false,
81082
81560
  error: null
81083
- } : prev);
81561
+ });
81084
81562
  prewarmedClipsRef.current.add(cacheKey);
81085
81563
  return;
81086
81564
  }