@optifye/dashboard-core 6.12.45 → 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,
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,
39444
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
  });
@@ -46314,6 +46510,13 @@ var FileManagerFilters = ({
46314
46510
  shift,
46315
46511
  count: node.count || 0
46316
46512
  });
46513
+ } else if (node.id === RECENT_FLOW_RED_STREAK_CLIP_TYPE2) {
46514
+ trackCoreEvent("Low Moments Clicked", {
46515
+ workspaceId,
46516
+ date,
46517
+ shift,
46518
+ count: node.count || 0
46519
+ });
46317
46520
  }
46318
46521
  if (node.id !== "idle_time" && idleLabelFilter) {
46319
46522
  setIdleLabelFilter(null);
@@ -47161,6 +47364,7 @@ function useClipsRealtimeUpdates({
47161
47364
  hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
47162
47365
  };
47163
47366
  }
47367
+ var LOW_EFFICIENCY_CATEGORY_ID = "recent_flow_red_streak";
47164
47368
  var parseFiniteNumber2 = (value) => {
47165
47369
  if (typeof value === "number" && Number.isFinite(value)) {
47166
47370
  return value;
@@ -47419,6 +47623,7 @@ var BottlenecksContent = ({
47419
47623
  totalOutput,
47420
47624
  enabled: isEffectiveShiftReady
47421
47625
  });
47626
+ const isLowMomentsCategoryAvailable = useMemo(() => Array.isArray(clipTypes) && clipTypes.some((type) => type?.type === LOW_EFFICIENCY_CATEGORY_ID || type?.id === LOW_EFFICIENCY_CATEGORY_ID), [clipTypes]);
47422
47627
  console.log("[BottlenecksContent] Clip types data:", {
47423
47628
  clipTypes,
47424
47629
  clipTypesLength: clipTypes?.length,
@@ -47469,20 +47674,20 @@ var BottlenecksContent = ({
47469
47674
  if (initialFilter) {
47470
47675
  return;
47471
47676
  }
47472
- const redStreakType = "recent_flow_red_streak";
47473
- if (dynamicCounts[redStreakType] > 0) {
47677
+ const redStreakType = LOW_EFFICIENCY_CATEGORY_ID;
47678
+ if (isLowMomentsCategoryAvailable && dynamicCounts[redStreakType] > 0) {
47474
47679
  setInitialFilter(redStreakType);
47475
47680
  setActiveFilter(redStreakType);
47476
47681
  activeFilterRef.current = redStreakType;
47477
47682
  return;
47478
47683
  }
47479
- if (defaultCategory) {
47684
+ if (defaultCategory && (defaultCategory !== LOW_EFFICIENCY_CATEGORY_ID || isLowMomentsCategoryAvailable)) {
47480
47685
  setInitialFilter(defaultCategory);
47481
47686
  setActiveFilter(defaultCategory);
47482
47687
  activeFilterRef.current = defaultCategory;
47483
47688
  return;
47484
47689
  }
47485
- }, [clipTypes, dynamicCounts, defaultCategory, initialFilter]);
47690
+ }, [clipTypes, dynamicCounts, defaultCategory, initialFilter, isLowMomentsCategoryAvailable]);
47486
47691
  const mergedCounts = useMemo(() => {
47487
47692
  return { ...dynamicCounts };
47488
47693
  }, [dynamicCounts]);
@@ -47701,7 +47906,7 @@ var BottlenecksContent = ({
47701
47906
  };
47702
47907
  const [cycleClips, redFlowClips, idleClips] = await Promise.all([
47703
47908
  fetchCategoryMetadata("cycle_completion"),
47704
- fetchCategoryMetadata("recent_flow_red_streak"),
47909
+ isLowMomentsCategoryAvailable ? fetchCategoryMetadata(LOW_EFFICIENCY_CATEGORY_ID) : Promise.resolve([]),
47705
47910
  fetchCategoryMetadata("idle_time")
47706
47911
  ]);
47707
47912
  const allClips = [...cycleClips, ...redFlowClips, ...idleClips].sort(
@@ -47715,7 +47920,7 @@ var BottlenecksContent = ({
47715
47920
  }
47716
47921
  };
47717
47922
  fetchTriageClips();
47718
- }, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, supabase, isEffectiveShiftReady]);
47923
+ }, [triageMode, workspaceId, effectiveDateString, effectiveShiftId, supabase, isEffectiveShiftReady, isLowMomentsCategoryAvailable]);
47719
47924
  useEffect(() => {
47720
47925
  if (!idleTimeVlmEnabled || !triageMode || triageClips.length === 0) {
47721
47926
  return;
@@ -47753,7 +47958,7 @@ var BottlenecksContent = ({
47753
47958
  }, [idleTimeVlmEnabled, triageClips, triageMode, getAuthToken4]);
47754
47959
  useEffect(() => {
47755
47960
  if (s3ClipsService && (mergedCounts[activeFilter] || 0) > 0) {
47756
- if (activeFilter === "recent_flow_red_streak") {
47961
+ if (activeFilter === LOW_EFFICIENCY_CATEGORY_ID) {
47757
47962
  return;
47758
47963
  }
47759
47964
  if (firstClip && firstClip.type === activeFilter) {
@@ -47776,12 +47981,12 @@ var BottlenecksContent = ({
47776
47981
  return ["fast-cycles", "slow-cycles", "longest-idles"].includes(categoryId);
47777
47982
  }, [isFastSlowClipFiltersEnabled]);
47778
47983
  const shouldUseMetadataNavigation = useCallback((categoryId) => {
47779
- 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";
47780
47985
  }, [idleClipSort, isPercentileCategory]);
47781
47986
  const getMetadataCacheKey = useCallback((categoryId) => {
47782
- const sortKey = categoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest";
47783
- return `${categoryId}-${effectiveDateString}-${effectiveShiftId}-${snapshotDateTime ?? "nosnap"}-${snapshotClipId ?? "nosnap"}-${sortKey}`;
47784
- }, [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]);
47785
47990
  const setVisibleCategoryMetadata = useCallback((categoryId, clips) => {
47786
47991
  if (activeFilterRef.current !== categoryId) {
47787
47992
  return false;
@@ -47789,7 +47994,7 @@ var BottlenecksContent = ({
47789
47994
  categoryMetadataRef.current = clips;
47790
47995
  setCategoryMetadata(clips);
47791
47996
  setCategoryMetadataCategoryId(clips.length > 0 ? categoryId : null);
47792
- 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);
47793
47998
  return true;
47794
47999
  }, [idleClipSort]);
47795
48000
  const applyMetadataSnapshot = useCallback((categoryId, clips, total) => {
@@ -47842,7 +48047,8 @@ var BottlenecksContent = ({
47842
48047
  if (activeFilter !== "fast-cycles" && activeFilter !== "slow-cycles") {
47843
48048
  return;
47844
48049
  }
47845
- 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;
47846
48052
  if (!fallbackFilter || fallbackFilter === activeFilter) {
47847
48053
  return;
47848
48054
  }
@@ -47851,7 +48057,25 @@ var BottlenecksContent = ({
47851
48057
  setCategoryMetadataSort(null);
47852
48058
  categoryMetadataRef.current = [];
47853
48059
  updateActiveFilter(fallbackFilter);
47854
- }, [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]);
47855
48079
  useCallback((categoryId) => {
47856
48080
  if (isPercentileCategory(categoryId)) {
47857
48081
  return categoryMetadata.length;
@@ -47903,6 +48127,16 @@ var BottlenecksContent = ({
47903
48127
  }
47904
48128
  return;
47905
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
+ }
47906
48140
  if (!isEffectiveShiftReady) {
47907
48141
  console.log("[BottlenecksContent] Skipping metadata load - shift/date not ready");
47908
48142
  if (activeFilterRef.current === categoryId) {
@@ -47914,10 +48148,10 @@ var BottlenecksContent = ({
47914
48148
  const cacheKey = getMetadataCacheKey(categoryId);
47915
48149
  const candidateLowMomentsPrefetch = lowMomentsPrefetch ?? null;
47916
48150
  const lowMomentsPrefetchMatchesScope = Boolean(
47917
- candidateLowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`)
48151
+ candidateLowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`)
47918
48152
  );
47919
- 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;
47920
- 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;
47921
48155
  if (lowMomentsPrefetchInFlight) {
47922
48156
  if (autoLoadFirstVideo && candidateLowMomentsPrefetch?.firstVideo) {
47923
48157
  applyPrefetchedFirstVideo(candidateLowMomentsPrefetch.firstVideo);
@@ -48008,7 +48242,7 @@ var BottlenecksContent = ({
48008
48242
  knownTotal: mergedCounts[categoryId] ?? null,
48009
48243
  snapshotDateTime,
48010
48244
  snapshotClipId,
48011
- 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"
48012
48246
  }),
48013
48247
  redirectReason: "session_expired"
48014
48248
  });
@@ -48102,19 +48336,19 @@ var BottlenecksContent = ({
48102
48336
  setIsCategoryLoading(false);
48103
48337
  }
48104
48338
  }
48105
- }, [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]);
48106
48340
  useEffect(() => {
48107
- if (activeFilter !== "recent_flow_red_streak") {
48341
+ if (activeFilter !== LOW_EFFICIENCY_CATEGORY_ID || !isLowMomentsCategoryAvailable) {
48108
48342
  return;
48109
48343
  }
48110
- if (!lowMomentsPrefetch?.key?.startsWith(`recent_flow_red_streak-${effectiveDateString}-${effectiveShiftId}-`)) {
48344
+ if (!lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`)) {
48111
48345
  return;
48112
48346
  }
48113
- 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)) {
48114
48348
  applyPrefetchedFirstVideo(lowMomentsPrefetch.firstVideo);
48115
48349
  }
48116
48350
  if (lowMomentsPrefetch.metadata.length > 0) {
48117
- applyMetadataSnapshot("recent_flow_red_streak", lowMomentsPrefetch.metadata, lowMomentsPrefetch.total);
48351
+ applyMetadataSnapshot(LOW_EFFICIENCY_CATEGORY_ID, lowMomentsPrefetch.metadata, lowMomentsPrefetch.total);
48118
48352
  if (isMountedRef.current) {
48119
48353
  setIsCategoryLoading(false);
48120
48354
  }
@@ -48126,11 +48360,13 @@ var BottlenecksContent = ({
48126
48360
  }, [
48127
48361
  activeFilter,
48128
48362
  lowMomentsPrefetch,
48363
+ workspaceId,
48129
48364
  effectiveDateString,
48130
48365
  effectiveShiftId,
48131
48366
  allVideos,
48132
48367
  applyPrefetchedFirstVideo,
48133
- applyMetadataSnapshot
48368
+ applyMetadataSnapshot,
48369
+ isLowMomentsCategoryAvailable
48134
48370
  ]);
48135
48371
  useEffect(() => {
48136
48372
  if (previousIdleClipSortRef.current === idleClipSort) {
@@ -48826,11 +49062,11 @@ var BottlenecksContent = ({
48826
49062
  }
48827
49063
  return currentPosition;
48828
49064
  }, [activeFilter, categoryMetadata.length, currentMetadataIndex, currentPosition, shouldUseMetadataNavigation]);
48829
- 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(
48830
49066
  activeFilter,
48831
49067
  categoryMetadataCategoryId,
48832
49068
  categoryMetadata
48833
- ), [activeFilter, categoryMetadata, categoryMetadataCategoryId, categoryMetadataSort, idleClipSort, lowMomentsPrefetch, effectiveDateString, effectiveShiftId]);
49069
+ ), [activeFilter, categoryMetadata, categoryMetadataCategoryId, categoryMetadataSort, idleClipSort, lowMomentsPrefetch, workspaceId, effectiveDateString, effectiveShiftId, isLowMomentsCategoryAvailable]);
48834
49070
  const classificationClipIds = useMemo(() => {
48835
49071
  if (!idleTimeVlmEnabled) {
48836
49072
  return [];
@@ -49603,7 +49839,7 @@ var BottlenecksContent = ({
49603
49839
  prefetchedClipMetadata: prefetchedExplorerMetadata,
49604
49840
  externallyManagedLoadingCategories: {
49605
49841
  recent_flow_red_streak: Boolean(
49606
- 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
49607
49843
  )
49608
49844
  },
49609
49845
  activeCategoryLoading: isCategoryLoading,
@@ -56876,10 +57112,12 @@ WorkspaceGridItem.displayName = "WorkspaceGridItem";
56876
57112
  var WorkspaceGrid = React148__default.memo(({
56877
57113
  workspaces,
56878
57114
  blueComparisonWorkspaces,
57115
+ worstPerformanceWorkspaceIds = [],
56879
57116
  isPdfMode = false,
56880
57117
  customWorkspacePositions,
56881
57118
  lineNames = {},
56882
57119
  lineOrder = [],
57120
+ activeSlideshowLineId = null,
56883
57121
  factoryView = "factory",
56884
57122
  line2Uuid = "line-2",
56885
57123
  className = "",
@@ -56890,7 +57128,8 @@ var WorkspaceGrid = React148__default.memo(({
56890
57128
  displayNames = {},
56891
57129
  onWorkspaceHover,
56892
57130
  onWorkspaceHoverEnd,
56893
- toolbarRightContent
57131
+ toolbarRightContent,
57132
+ displayMode = "all"
56894
57133
  }) => {
56895
57134
  const mapViewEnabled = false;
56896
57135
  const [viewMode, setViewMode] = useState(() => {
@@ -56948,13 +57187,16 @@ var WorkspaceGrid = React148__default.memo(({
56948
57187
  {
56949
57188
  workspaces,
56950
57189
  blueComparisonWorkspaces,
57190
+ worstPerformanceWorkspaceIds,
56951
57191
  lineNames,
56952
57192
  lineOrder,
57193
+ activeSlideshowLineId,
56953
57194
  videoSources,
56954
57195
  videoStreamsByWorkspaceId,
56955
57196
  videoStreamsLoading,
56956
57197
  displayNames,
56957
57198
  legend,
57199
+ displayMode,
56958
57200
  onWorkspaceHover,
56959
57201
  onWorkspaceHoverEnd
56960
57202
  }
@@ -67093,6 +67335,31 @@ var NotificationService = class {
67093
67335
  function createNotificationService(supabaseClient) {
67094
67336
  return new NotificationService(supabaseClient);
67095
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";
67096
67363
  var KPISection2 = KPISection;
67097
67364
  var DEBUG_DASHBOARD_LOGS3 = process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
67098
67365
  var logDebug3 = (...args) => {
@@ -67103,6 +67370,9 @@ var EMPTY_LINE_IDS = [];
67103
67370
  var EMPTY_WORKSPACES = [];
67104
67371
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
67105
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";
67106
67376
  var ALL_GREEN_CELEBRATION_SEEN_PREFIX = "optifye:all-green-celebration:v1:";
67107
67377
  var ALL_GREEN_MILESTONE_SEEN_PREFIX = "optifye:all-green-milestone:v1:";
67108
67378
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
@@ -67232,6 +67502,31 @@ function HomeView({
67232
67502
  const [isLineSelectorOpen, setIsLineSelectorOpen] = useState(false);
67233
67503
  const [pendingSelectedLineIds, setPendingSelectedLineIds] = useState([]);
67234
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());
67235
67530
  useEffect(() => {
67236
67531
  if (isLineSelectorOpen) {
67237
67532
  setPendingSelectedLineIds(selectedLineIds);
@@ -67258,6 +67553,13 @@ function HomeView({
67258
67553
  console.warn("Failed to save line filter to sessionStorage:", error);
67259
67554
  }
67260
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]);
67261
67563
  useEffect(() => {
67262
67564
  if (!isLineSelectorOpen) {
67263
67565
  return;
@@ -67272,6 +67574,20 @@ function HomeView({
67272
67574
  document.removeEventListener("mousedown", handleClickOutside);
67273
67575
  };
67274
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]);
67275
67591
  const primarySelectedLineId = selectedLineIds[0] || defaultHomeLineId;
67276
67592
  const isMultiLineSelection = selectedLineIds.length > 1;
67277
67593
  const selectedLineIdsKey = selectedLineIds.join(",");
@@ -67574,6 +67890,88 @@ function HomeView({
67574
67890
  })),
67575
67891
  [currentWorkspaceMetrics, activeBreakLineIds]
67576
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]);
67577
67975
  const [breakNotificationsDismissed, setBreakNotificationsDismissed] = useState(false);
67578
67976
  useEffect(() => {
67579
67977
  if (currentActiveBreaks.length > 0) {
@@ -67585,6 +67983,11 @@ function HomeView({
67585
67983
  () => workspaceMetricsWithBreakState.filter((workspace) => Boolean(workspace.workspace_uuid || workspace.workspace_name)).length,
67586
67984
  [workspaceMetricsWithBreakState]
67587
67985
  );
67986
+ const worstPerformanceWorkspaceIds = useMemo(
67987
+ () => Array.from(selectWorstPerformanceWorkspaceIds(workspaceMetricsWithBreakState)),
67988
+ [workspaceMetricsWithBreakState]
67989
+ );
67990
+ const activeWorstPerformanceWorkspaceIds = displayMode === "worst_workstations" ? worstPerformanceWorkspaceIds : EMPTY_LINE_IDS;
67588
67991
  const allGreenCelebrationSignature = useMemo(() => {
67589
67992
  const workspaceSignature = workspaceMetricsWithBreakState.map((workspace) => workspace.workspace_uuid || `${workspace.line_id}:${workspace.workspace_name}`).filter(Boolean).sort().join(",");
67590
67993
  return `${selectedLineIds.join(",")}::${workspaceSignature}`;
@@ -68221,6 +68624,10 @@ function HomeView({
68221
68624
  selection_mode: isAllLinesSelection(normalizedLineIds) ? "all" : normalizedLineIds.length === 1 ? "single" : "custom",
68222
68625
  line_name: getLineSelectionLabel(normalizedLineIds)
68223
68626
  });
68627
+ trackCoreEvent("Dashboard Filter Selected", {
68628
+ filter_type: "Line Filter",
68629
+ filter_value: getLineSelectionLabel(normalizedLineIds)
68630
+ });
68224
68631
  }, [factoryViewId, getLineSelectionLabel, getTrackedLineScope, selectedLineIds, selectedLineIdsKey, visibleLineIds]);
68225
68632
  useCallback(() => {
68226
68633
  updateSelectedLineIds(visibleLineIds);
@@ -68275,7 +68682,7 @@ function HomeView({
68275
68682
  "aria-expanded": isLineSelectorOpen,
68276
68683
  "aria-label": "Select lines",
68277
68684
  children: [
68278
- /* @__PURE__ */ jsx("span", { className: "truncate", children: getLineSelectionLabel(selectedLineIds) }),
68685
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: displayMode === "slideshow" && slideshowActiveLineId ? getLineSelectionLabel([slideshowActiveLineId]) : getLineSelectionLabel(selectedLineIds) }),
68279
68686
  /* @__PURE__ */ jsx(ChevronDown, { className: `h-4 w-4 text-slate-400 transition-transform ${isLineSelectorOpen ? "rotate-180" : ""}` })
68280
68687
  ]
68281
68688
  }
@@ -68371,10 +68778,78 @@ function HomeView({
68371
68778
  selectedLineIds,
68372
68779
  pendingSelectedLineIds,
68373
68780
  lineSelectorSignalStatusByLine,
68781
+ displayMode,
68782
+ slideshowActiveLineId,
68374
68783
  visibleLineIds,
68375
68784
  updateSelectedLineIds,
68376
68785
  isAllLinesSelection
68377
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]);
68378
68853
  const gridToolbarControls = useMemo(() => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2", children: [
68379
68854
  /* @__PURE__ */ jsx(AnimatePresence, { children: visibleAllGreenStreakDisplay ? /* @__PURE__ */ jsxs(
68380
68855
  motion.div,
@@ -68392,8 +68867,10 @@ function HomeView({
68392
68867
  },
68393
68868
  visibleAllGreenStreakDisplay.startedAt
68394
68869
  ) : null }),
68395
- lineSelectorComponent
68870
+ lineSelectorComponent,
68871
+ displayModeSelectorComponent
68396
68872
  ] }), [
68873
+ displayModeSelectorComponent,
68397
68874
  lineSelectorComponent,
68398
68875
  visibleAllGreenStreakDisplay
68399
68876
  ]);
@@ -68570,14 +69047,17 @@ function HomeView({
68570
69047
  children: React148__default.createElement(WorkspaceGrid, {
68571
69048
  workspaces: workspaceMetricsWithBreakState,
68572
69049
  blueComparisonWorkspaces: currentBlueComparisonWorkspaceMetrics || workspaceMetricsWithBreakState,
69050
+ worstPerformanceWorkspaceIds: activeWorstPerformanceWorkspaceIds,
68573
69051
  lineNames: mergedLineNames,
68574
- lineOrder: selectedLineIds,
69052
+ lineOrder: displayMode === "slideshow" ? slideshowLineIds : selectedLineIds,
69053
+ activeSlideshowLineId: displayMode === "slideshow" ? slideshowActiveLineId : null,
68575
69054
  factoryView: factoryViewId,
68576
69055
  legend: effectiveEfficiencyLegend,
68577
69056
  videoSources,
68578
69057
  videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
68579
69058
  videoStreamsLoading: currentVideoStreamsLoading,
68580
69059
  displayNames: metricsDisplayNames,
69060
+ displayMode,
68581
69061
  hasFlowBuffers,
68582
69062
  className: "h-full",
68583
69063
  toolbarRightContent: gridToolbarControls,
@@ -68604,14 +69084,17 @@ function HomeView({
68604
69084
  workspaces: [],
68605
69085
  // Show empty grid while loading
68606
69086
  blueComparisonWorkspaces: [],
69087
+ worstPerformanceWorkspaceIds: [],
68607
69088
  lineNames: mergedLineNames,
68608
- lineOrder: selectedLineIds,
69089
+ lineOrder: displayMode === "slideshow" ? slideshowLineIds : selectedLineIds,
69090
+ activeSlideshowLineId: displayMode === "slideshow" ? slideshowActiveLineId : null,
68609
69091
  factoryView: factoryViewId,
68610
69092
  legend: effectiveEfficiencyLegend,
68611
69093
  videoSources,
68612
69094
  videoStreamsByWorkspaceId: currentVideoStreamsByWorkspaceId,
68613
69095
  videoStreamsLoading: currentVideoStreamsLoading,
68614
69096
  displayNames: metricsDisplayNames,
69097
+ displayMode,
68615
69098
  hasFlowBuffers,
68616
69099
  className: "h-full",
68617
69100
  toolbarRightContent: gridToolbarControls,
@@ -81052,9 +81535,11 @@ var WorkspaceDetailView = ({
81052
81535
  const prefetchLowMoments = async () => {
81053
81536
  try {
81054
81537
  const initData = await s3Service.getClipsInit(workspaceId, resolvedDate, resolvedShiftId, totalOutput);
81055
- 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;
81056
81540
  const lowMomentsKey = [
81057
81541
  "recent_flow_red_streak",
81542
+ workspaceId,
81058
81543
  resolvedDate,
81059
81544
  resolvedShiftId,
81060
81545
  initData?.snapshotDateTime ?? "nosnap",
@@ -81065,15 +81550,15 @@ var WorkspaceDetailView = ({
81065
81550
  return;
81066
81551
  }
81067
81552
  const initFirstVideo = initData?.firstClips?.recent_flow_red_streak ?? null;
81068
- if (lowMomentsCount <= 0) {
81069
- setLowMomentsPrefetch((prev) => prev?.key === lowMomentsKey ? {
81553
+ if (!hasLowMomentsCategory || lowMomentsCount <= 0) {
81554
+ setLowMomentsPrefetch({
81070
81555
  key: lowMomentsKey,
81071
81556
  metadata: [],
81072
81557
  firstVideo: null,
81073
81558
  total: 0,
81074
81559
  loading: false,
81075
81560
  error: null
81076
- } : prev);
81561
+ });
81077
81562
  prewarmedClipsRef.current.add(cacheKey);
81078
81563
  return;
81079
81564
  }