@optifye/dashboard-core 6.9.5 → 6.9.7

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
@@ -11,7 +11,7 @@ import Hls2 from 'hls.js';
11
11
  import useSWR from 'swr';
12
12
  import { noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds, memo as memo$1 } from 'motion-utils';
13
13
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
14
- import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, Pause, Play, XCircle, ChevronLeft, ChevronRight, Maximize2, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, HelpCircle, Sliders, Activity, Layers, Filter, Search, Edit2, AlertTriangle, CheckCircle, Building2, Mail, Users, User, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, Package, UserPlus, Settings, LifeBuoy, EyeOff, Eye, MoreVertical, UserCog, Zap, Shield, UserCircle } from 'lucide-react';
14
+ import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, XCircle, Maximize2, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, HelpCircle, Sliders, Activity, Layers, Filter, Search, Edit2, AlertTriangle, CheckCircle, Building2, Mail, Users, User, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, Package, UserPlus, Settings, LifeBuoy, EyeOff, Eye, MoreVertical, UserCog, Zap, Shield, UserCircle } from 'lucide-react';
15
15
  import { toast } from 'sonner';
16
16
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
17
17
  import { Slot } from '@radix-ui/react-slot';
@@ -9573,8 +9573,13 @@ var getAllWorkspaceDisplayNamesAsync = async (companyId, lineId) => {
9573
9573
  return { ...runtimeWorkspaceDisplayNames[lineId] };
9574
9574
  }
9575
9575
  const allNames = {};
9576
- Object.values(runtimeWorkspaceDisplayNames).forEach((lineNames) => {
9577
- Object.assign(allNames, lineNames);
9576
+ Object.entries(runtimeWorkspaceDisplayNames).forEach(([lineId2, lineNames]) => {
9577
+ Object.entries(lineNames).forEach(([workspaceId, displayName]) => {
9578
+ allNames[`${lineId2}_${workspaceId}`] = displayName;
9579
+ if (!allNames[workspaceId]) {
9580
+ allNames[workspaceId] = displayName;
9581
+ }
9582
+ });
9578
9583
  });
9579
9584
  return allNames;
9580
9585
  };
@@ -24699,7 +24704,11 @@ var VideoGridView = React23__default.memo(({
24699
24704
  isVeryLowEfficiency,
24700
24705
  cropping: workspaceCropping,
24701
24706
  canvasFps: canvasConfig?.fps,
24702
- displayName: displayNames[workspace.workspace_name] || workspace.workspace_name,
24707
+ displayName: (
24708
+ // Create line-aware lookup key: lineId_workspaceName
24709
+ // This ensures correct mapping when multiple lines have same workspace names
24710
+ displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || displayNames[workspace.workspace_name] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id)
24711
+ ),
24703
24712
  useRAF: canvasConfig?.useRAF,
24704
24713
  compact: !selectedLine,
24705
24714
  onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(workspaceId) : void 0,
@@ -24748,7 +24757,7 @@ var MapGridView = React23__default.memo(({
24748
24757
  efficiency: workspace.efficiency,
24749
24758
  action_count: workspace.action_count
24750
24759
  });
24751
- const displayName = displayNames[workspace.workspace_name] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
24760
+ const displayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || displayNames[workspace.workspace_name] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
24752
24761
  const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id);
24753
24762
  router.push(`/workspace/${workspaceId}${navParams}`);
24754
24763
  }, [router, displayNames]);
@@ -24777,7 +24786,7 @@ var MapGridView = React23__default.memo(({
24777
24786
  if (!workspace) return null;
24778
24787
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
24779
24788
  getPerformanceColor(workspace.efficiency);
24780
- const workspaceDisplayName = displayNames[workspace.workspace_name] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
24789
+ const workspaceDisplayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || displayNames[workspace.workspace_name] || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
24781
24790
  return /* @__PURE__ */ jsx(
24782
24791
  motion.div,
24783
24792
  {
@@ -25623,18 +25632,30 @@ var BreakNotificationPopup = ({
25623
25632
  className = "",
25624
25633
  lineNames = {}
25625
25634
  }) => {
25626
- const [isDismissed, setIsDismissed] = useState(false);
25635
+ const [currentIndex, setCurrentIndex] = useState(0);
25636
+ const [visibleBreaks, setVisibleBreaks] = useState(activeBreaks);
25627
25637
  const [currentTime, setCurrentTime] = useState(/* @__PURE__ */ new Date());
25638
+ useEffect(() => {
25639
+ setVisibleBreaks(activeBreaks);
25640
+ if (currentIndex >= activeBreaks.length) {
25641
+ setCurrentIndex(Math.max(0, activeBreaks.length - 1));
25642
+ }
25643
+ }, [activeBreaks, currentIndex]);
25628
25644
  useEffect(() => {
25629
25645
  const timer = setInterval(() => {
25630
25646
  setCurrentTime(/* @__PURE__ */ new Date());
25631
25647
  }, 6e4);
25632
25648
  return () => clearInterval(timer);
25633
25649
  }, []);
25634
- const handleDismiss = () => {
25635
- setIsDismissed(true);
25650
+ const handleDismissAll = () => {
25636
25651
  onDismiss?.();
25637
25652
  };
25653
+ const handleNext = () => {
25654
+ setCurrentIndex((prev) => (prev + 1) % visibleBreaks.length);
25655
+ };
25656
+ const handlePrevious = () => {
25657
+ setCurrentIndex((prev) => (prev - 1 + visibleBreaks.length) % visibleBreaks.length);
25658
+ };
25638
25659
  const formatTime3 = (minutes) => {
25639
25660
  const hours = Math.floor(minutes / 60);
25640
25661
  const mins = minutes % 60;
@@ -25643,69 +25664,130 @@ var BreakNotificationPopup = ({
25643
25664
  }
25644
25665
  return `${mins}m`;
25645
25666
  };
25646
- if (!isVisible || isDismissed || activeBreaks.length === 0) {
25667
+ const formatTo12Hour = (time24) => {
25668
+ const [hours, minutes] = time24.split(":").map(Number);
25669
+ if (isNaN(hours) || isNaN(minutes)) {
25670
+ return time24;
25671
+ }
25672
+ const period = hours >= 12 ? "PM" : "AM";
25673
+ const hours12 = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
25674
+ return `${hours12}:${minutes.toString().padStart(2, "0")} ${period}`;
25675
+ };
25676
+ if (!isVisible || visibleBreaks.length === 0) {
25647
25677
  return null;
25648
25678
  }
25649
- return /* @__PURE__ */ jsx(AnimatePresence, { children: activeBreaks.map((breakItem, index) => /* @__PURE__ */ jsx(
25650
- motion.div,
25651
- {
25652
- initial: { opacity: 0, x: 100, y: -20 },
25653
- animate: { opacity: 1, x: 0, y: 0 },
25654
- exit: { opacity: 0, x: 100, y: -20 },
25655
- transition: { duration: 0.3, ease: "easeOut", delay: index * 0.1 },
25656
- className: `fixed right-4 z-50 max-w-xs w-full ${className}`,
25657
- style: { top: `${6 + index * 12}rem` },
25658
- children: /* @__PURE__ */ jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-lg overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
25659
- /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
25660
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(AxelOrb, { size: "md" }) }),
25661
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
25662
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
25663
- /* @__PURE__ */ jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
25664
- breakItem.remarks || "Break",
25665
- (activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500 ml-1", children: [
25666
- "\u2022 ",
25667
- lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}`
25668
- ] })
25679
+ const currentBreak = visibleBreaks[currentIndex];
25680
+ return /* @__PURE__ */ jsx(AnimatePresence, { children: /* @__PURE__ */ jsxs("div", { className: `fixed right-4 top-24 z-50 max-w-xs w-full ${className}`, children: [
25681
+ visibleBreaks.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
25682
+ /* @__PURE__ */ jsx(
25683
+ "div",
25684
+ {
25685
+ className: "absolute inset-0 bg-white rounded-lg border border-gray-300 shadow-lg",
25686
+ style: {
25687
+ transform: "translateY(12px) scale(0.94)",
25688
+ opacity: 0.6,
25689
+ zIndex: -2
25690
+ }
25691
+ }
25692
+ ),
25693
+ visibleBreaks.length > 2 && /* @__PURE__ */ jsx(
25694
+ "div",
25695
+ {
25696
+ className: "absolute inset-0 bg-white rounded-lg border border-gray-200 shadow-md",
25697
+ style: {
25698
+ transform: "translateY(6px) scale(0.97)",
25699
+ opacity: 0.8,
25700
+ zIndex: -1
25701
+ }
25702
+ }
25703
+ )
25704
+ ] }),
25705
+ /* @__PURE__ */ jsx(
25706
+ motion.div,
25707
+ {
25708
+ initial: { opacity: 0, x: 100, y: -20 },
25709
+ animate: { opacity: 1, x: 0, y: 0 },
25710
+ exit: { opacity: 0, x: -100, y: -20 },
25711
+ transition: { duration: 0.3, ease: "easeOut" },
25712
+ className: "relative z-10",
25713
+ children: /* @__PURE__ */ jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-xl overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
25714
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
25715
+ /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(AxelOrb, { size: "md" }) }),
25716
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
25717
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
25718
+ /* @__PURE__ */ jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
25719
+ currentBreak.remarks || "Break",
25720
+ lineNames[currentBreak.lineId] && /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500 ml-1", children: [
25721
+ "\u2022 ",
25722
+ lineNames[currentBreak.lineId]
25723
+ ] })
25724
+ ] }),
25725
+ /* @__PURE__ */ jsx(Coffee, { className: "w-4 h-4 text-amber-500" })
25669
25726
  ] }),
25670
- /* @__PURE__ */ jsx(Coffee, { className: "w-4 h-4 text-amber-500" })
25671
- ] }),
25672
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600 leading-relaxed mb-2", children: [
25673
- "Currently active from ",
25674
- breakItem.startTime,
25675
- " to ",
25676
- breakItem.endTime,
25677
- ". ",
25678
- formatTime3(breakItem.elapsedMinutes),
25679
- " elapsed of ",
25680
- formatTime3(breakItem.duration),
25681
- " total."
25682
- ] }),
25683
- /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsx(
25684
- "div",
25685
- {
25686
- className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
25687
- style: {
25688
- width: `${Math.min(100, Math.max(0, breakItem.elapsedMinutes / breakItem.duration * 100))}%`
25727
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600 mb-1", children: [
25728
+ formatTo12Hour(currentBreak.startTime),
25729
+ " - ",
25730
+ formatTo12Hour(currentBreak.endTime)
25731
+ ] }),
25732
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500 mb-2", children: [
25733
+ formatTime3(currentBreak.elapsedMinutes),
25734
+ " elapsed of ",
25735
+ formatTime3(currentBreak.duration),
25736
+ " total"
25737
+ ] }),
25738
+ /* @__PURE__ */ jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsx(
25739
+ "div",
25740
+ {
25741
+ className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
25742
+ style: {
25743
+ width: `${Math.min(100, Math.max(0, currentBreak.elapsedMinutes / currentBreak.duration * 100))}%`
25744
+ }
25689
25745
  }
25690
- }
25691
- ) }) })
25692
- ] })
25693
- ] }),
25694
- /* @__PURE__ */ jsx(
25695
- "button",
25696
- {
25697
- onClick: handleDismiss,
25698
- onTouchStart: () => {
25699
- },
25700
- className: "ml-2 text-gray-400 hover:text-gray-600 transition-colors p-2 sm:p-1 rounded-full hover:bg-gray-100 active:bg-gray-200 touch-manipulation min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0 flex items-center justify-center flex-shrink-0",
25701
- "aria-label": "Dismiss notification",
25702
- children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
25703
- }
25704
- )
25705
- ] }) }) })
25706
- },
25707
- `${breakItem.lineId}-${breakItem.startTime}-${index}`
25708
- )) });
25746
+ ) }),
25747
+ visibleBreaks.length > 1 && /* @__PURE__ */ jsx("div", { className: "mt-3 pt-2 border-t border-gray-100", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
25748
+ /* @__PURE__ */ jsx(
25749
+ "button",
25750
+ {
25751
+ onClick: handlePrevious,
25752
+ className: "p-1 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-full transition-colors",
25753
+ "aria-label": "Previous break",
25754
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" })
25755
+ }
25756
+ ),
25757
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-500", children: [
25758
+ currentIndex + 1,
25759
+ " of ",
25760
+ visibleBreaks.length,
25761
+ " breaks"
25762
+ ] }),
25763
+ /* @__PURE__ */ jsx(
25764
+ "button",
25765
+ {
25766
+ onClick: handleNext,
25767
+ className: "p-1 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-full transition-colors",
25768
+ "aria-label": "Next break",
25769
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4" })
25770
+ }
25771
+ )
25772
+ ] }) })
25773
+ ] })
25774
+ ] }),
25775
+ /* @__PURE__ */ jsx(
25776
+ "button",
25777
+ {
25778
+ onClick: handleDismissAll,
25779
+ onTouchStart: () => {
25780
+ },
25781
+ className: "ml-2 text-gray-400 hover:text-gray-600 transition-colors p-2 sm:p-1 rounded-full hover:bg-gray-100 active:bg-gray-200 touch-manipulation min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0 flex items-center justify-center flex-shrink-0",
25782
+ "aria-label": "Dismiss all breaks",
25783
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
25784
+ }
25785
+ )
25786
+ ] }) }) })
25787
+ },
25788
+ currentIndex
25789
+ )
25790
+ ] }) });
25709
25791
  };
25710
25792
  async function fetchAxelNotifications() {
25711
25793
  try {
@@ -26669,8 +26751,11 @@ var VideoPlayer = React23__default.forwardRef(({
26669
26751
  experimentalLLHLS: false,
26670
26752
  // Disable Low Latency HLS for VOD
26671
26753
  // Connection settings
26672
- enableWorker: true,
26673
- // Use web worker for better performance
26754
+ // Chrome 130+ started throwing "Cannot perform Construct on a detached ArrayBuffer"
26755
+ // whenever the transmux worker tried to rehydrate transferred buffers that originated
26756
+ // from Blob-based playlists. Disabling the worker keeps playback stable at the cost
26757
+ // of slightly higher main-thread usage, which is acceptable for the dashboard usage.
26758
+ enableWorker: false,
26674
26759
  progressive: true,
26675
26760
  // Progressive download
26676
26761
  // Adaptive bitrate settings (if multi-quality available)
@@ -27680,10 +27765,30 @@ function useWorkspaceCrop(workspaceId) {
27680
27765
  }, [workspaceId]);
27681
27766
  return { crop, isLoading, error };
27682
27767
  }
27683
- var getSeverityIcon = (severity, categoryId) => {
27768
+ var parseCycleTime = (value) => {
27769
+ if (typeof value === "number" && Number.isFinite(value)) {
27770
+ return value;
27771
+ }
27772
+ if (typeof value === "string") {
27773
+ const parsed = Number(value);
27774
+ return Number.isFinite(parsed) ? parsed : null;
27775
+ }
27776
+ return null;
27777
+ };
27778
+ var extractCycleTimeSeconds = (clip) => {
27779
+ return parseCycleTime(clip?.cycleTimeSeconds) ?? parseCycleTime(clip?.cycle_time_seconds) ?? parseCycleTime(clip?.duration) ?? parseCycleTime(clip?.original_task_metadata?.cycle_time) ?? null;
27780
+ };
27781
+ var getSeverityIcon = (severity, categoryId, cycleTimeSeconds, targetCycleTime) => {
27684
27782
  if (categoryId === "idle_time" || categoryId === "low_value" || categoryId === "longest-idles") {
27685
27783
  return /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 text-red-500" });
27686
27784
  }
27785
+ if (categoryId === "cycle_completion" && targetCycleTime && targetCycleTime > 0 && cycleTimeSeconds !== null && cycleTimeSeconds !== void 0) {
27786
+ if (cycleTimeSeconds <= targetCycleTime) {
27787
+ return /* @__PURE__ */ jsx(CheckCircle, { className: "h-3 w-3 text-green-500" });
27788
+ } else {
27789
+ return /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 text-red-500" });
27790
+ }
27791
+ }
27687
27792
  switch (severity) {
27688
27793
  case "high":
27689
27794
  return /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 text-red-500" });
@@ -27738,7 +27843,8 @@ var FileManagerFilters = ({
27738
27843
  workspaceId,
27739
27844
  date,
27740
27845
  shift,
27741
- className = ""
27846
+ className = "",
27847
+ targetCycleTime = null
27742
27848
  }) => {
27743
27849
  const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
27744
27850
  const [startTime, setStartTime] = useState("");
@@ -27758,6 +27864,26 @@ var FileManagerFilters = ({
27758
27864
  const [percentileCounts, setPercentileCounts] = useState({});
27759
27865
  const [percentileClips, setPercentileClips] = useState({});
27760
27866
  const [loadingPercentile, setLoadingPercentile] = useState(false);
27867
+ const resolvedTargetCycleTime = targetCycleTime && targetCycleTime > 0 ? targetCycleTime : null;
27868
+ const getClipBadge = useCallback((node) => {
27869
+ if (node.categoryId === "idle_time" || node.categoryId === "low_value") {
27870
+ return { text: "Idle", className: "bg-red-100 text-red-700" };
27871
+ }
27872
+ if (node.categoryId === "cycle_completion" && resolvedTargetCycleTime && node.cycleTimeSeconds !== void 0 && node.cycleTimeSeconds !== null) {
27873
+ const isFast = node.cycleTimeSeconds <= resolvedTargetCycleTime;
27874
+ return {
27875
+ text: isFast ? "Fast" : "Slow",
27876
+ className: isFast ? "bg-green-100 text-green-700" : "bg-red-100 text-red-700"
27877
+ };
27878
+ }
27879
+ if (node.severity === "high") {
27880
+ return { text: "Slow", className: "bg-red-100 text-red-700" };
27881
+ }
27882
+ if (node.severity === "medium") {
27883
+ return { text: "Average", className: "bg-yellow-100 text-yellow-700" };
27884
+ }
27885
+ return { text: "Fast", className: "bg-green-100 text-green-700" };
27886
+ }, [resolvedTargetCycleTime]);
27761
27887
  const fetchClipMetadata = useCallback(async (categoryId, page = 1) => {
27762
27888
  if (!workspaceId || !date || shift === void 0) {
27763
27889
  console.warn("[FileManager] Missing required params for clip metadata fetch");
@@ -27995,18 +28121,20 @@ var FileManagerFilters = ({
27995
28121
  timeZone: timezone
27996
28122
  // Use database timezone for display
27997
28123
  });
28124
+ const cycleTime = extractCycleTimeSeconds(clip);
27998
28125
  return {
27999
28126
  id: clip.id,
28000
28127
  label: `${timeString}${clip.duration && category.id !== "idle_time" ? ` - (${clip.duration.toFixed(1)}s)` : ""}`,
28001
28128
  type: "video",
28002
- icon: getSeverityIcon(clip.severity, category.id),
28129
+ icon: getSeverityIcon(clip.severity, category.id, cycleTime, resolvedTargetCycleTime),
28003
28130
  timestamp: clip.clip_timestamp,
28004
28131
  severity: clip.severity,
28005
28132
  clipId: clip.clipId,
28006
28133
  // Store stable UUID for navigation
28007
28134
  categoryId: category.id,
28008
- clipPosition: index + 1
28135
+ clipPosition: index + 1,
28009
28136
  // Store 1-based position
28137
+ cycleTimeSeconds: cycleTime
28010
28138
  };
28011
28139
  });
28012
28140
  regularCategoryNodes.push({
@@ -28045,15 +28173,17 @@ var FileManagerFilters = ({
28045
28173
  minute: "2-digit",
28046
28174
  timeZone: timezone
28047
28175
  });
28176
+ const cycleTime = extractCycleTimeSeconds(clip);
28048
28177
  return {
28049
28178
  id: clip.id,
28050
28179
  label: `${timeString}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28051
28180
  type: "video",
28052
- icon: getSeverityIcon(clip.severity, "fast-cycles"),
28181
+ icon: getSeverityIcon(clip.severity, "fast-cycles", cycleTime, resolvedTargetCycleTime),
28053
28182
  timestamp: clip.creation_timestamp,
28054
28183
  severity: clip.severity,
28055
28184
  clipId: clip.id,
28056
- categoryId: "fast-cycles"
28185
+ categoryId: "fast-cycles",
28186
+ cycleTimeSeconds: cycleTime
28057
28187
  };
28058
28188
  })
28059
28189
  },
@@ -28075,15 +28205,17 @@ var FileManagerFilters = ({
28075
28205
  minute: "2-digit",
28076
28206
  timeZone: timezone
28077
28207
  });
28208
+ const cycleTime = extractCycleTimeSeconds(clip);
28078
28209
  return {
28079
28210
  id: clip.id,
28080
28211
  label: `${timeString}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28081
28212
  type: "video",
28082
- icon: getSeverityIcon(clip.severity, "slow-cycles"),
28213
+ icon: getSeverityIcon(clip.severity, "slow-cycles", cycleTime, resolvedTargetCycleTime),
28083
28214
  timestamp: clip.creation_timestamp,
28084
28215
  severity: clip.severity,
28085
28216
  clipId: clip.id,
28086
- categoryId: "slow-cycles"
28217
+ categoryId: "slow-cycles",
28218
+ cycleTimeSeconds: cycleTime
28087
28219
  };
28088
28220
  })
28089
28221
  }
@@ -28204,7 +28336,10 @@ var FileManagerFilters = ({
28204
28336
  /* @__PURE__ */ jsx("div", { className: `font-semibold tracking-tight ${node.type === "category" || node.type === "percentile-category" ? "text-slate-800 text-sm" : "text-slate-700 text-xs"} ${isCurrentVideo ? "text-blue-700 font-bold" : ""} group-hover:text-slate-900 transition-colors duration-200`, children: node.label }),
28205
28337
  node.type === "category" && categories.find((c) => c.id === node.id)?.description && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: categories.find((c) => c.id === node.id)?.description }),
28206
28338
  node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
28207
- node.type === "video" && node.severity && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: /* @__PURE__ */ jsx("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${node.categoryId === "idle_time" || node.categoryId === "low_value" ? "bg-red-100 text-red-700" : node.severity === "high" ? "bg-red-100 text-red-700" : node.severity === "medium" ? "bg-yellow-100 text-yellow-700" : "bg-green-100 text-green-700"}`, children: node.categoryId === "idle_time" || node.categoryId === "low_value" ? "Idle" : node.severity === "low" ? "Fast" : node.severity === "medium" ? "Average" : "Slow" }) })
28339
+ node.type === "video" && (node.severity || node.categoryId === "cycle_completion" || node.categoryId === "idle_time" || node.categoryId === "low_value") && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: (() => {
28340
+ const badge = getClipBadge(node);
28341
+ return /* @__PURE__ */ jsx("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${badge.className}`, children: badge.text });
28342
+ })() })
28208
28343
  ] }),
28209
28344
  node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsx("span", { className: `px-2.5 py-1 text-sm font-bold rounded-lg shadow-sm border backdrop-blur-sm flex-shrink-0 group-hover:scale-105 transition-all duration-200 ${colorClasses ? `${colorClasses.bg} ${colorClasses.text} ${colorClasses.border} bg-opacity-80` : "bg-slate-100/80 text-slate-700 border-slate-200/60"}`, children: node.count }) })
28210
28345
  ] })
@@ -28871,6 +29006,19 @@ var BottlenecksContent = ({
28871
29006
  }
28872
29007
  }, [shift, date, timezone, shiftConfig, workspaceId]);
28873
29008
  const { crop: workspaceCrop} = useWorkspaceCrop(workspaceId);
29009
+ const { metrics: workspaceMetrics } = useWorkspaceDetailedMetrics(
29010
+ workspaceId,
29011
+ date,
29012
+ shift !== void 0 && shift !== null ? Number(shift) : void 0
29013
+ );
29014
+ const workspaceTargetCycleTimeRaw = workspaceMetrics?.ideal_cycle_time;
29015
+ const workspaceTargetCycleTime = (() => {
29016
+ if (workspaceTargetCycleTimeRaw === void 0 || workspaceTargetCycleTimeRaw === null) {
29017
+ return null;
29018
+ }
29019
+ const numericValue = typeof workspaceTargetCycleTimeRaw === "number" ? workspaceTargetCycleTimeRaw : Number(workspaceTargetCycleTimeRaw);
29020
+ return Number.isFinite(numericValue) ? numericValue : null;
29021
+ })();
28874
29022
  const videoRef = useRef(null);
28875
29023
  const [initialFilter, setInitialFilter] = useState("");
28876
29024
  const currentIndexRef = useRef(0);
@@ -29813,21 +29961,33 @@ var BottlenecksContent = ({
29813
29961
  return () => window.removeEventListener("keydown", handleEscape);
29814
29962
  }
29815
29963
  }, [isFullscreen, exitFullscreen]);
29816
- const getClipTypeLabel = (video) => {
29964
+ const getClipTypeLabel = useCallback((video) => {
29817
29965
  if (!video) return "";
29818
29966
  const currentFilter = activeFilterRef.current;
29967
+ const targetCycleTime = workspaceTargetCycleTime || 0;
29819
29968
  if (isPercentileCategory(currentFilter)) {
29820
29969
  const percentileValue = video.percentile?.toFixed(1);
29821
- switch (currentFilter) {
29822
- case "fast-cycles":
29823
- return `\u26A1 Fast Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29824
- case "slow-cycles":
29825
- return `\u{1F40C} Slow Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29826
- case "longest-idles":
29827
- return `\u23F0 Long Idle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29828
- default:
29829
- return video.description || "Performance Clip";
29970
+ const cycleTime = video.cycle_time_seconds || 0;
29971
+ if (currentFilter === "fast-cycles" || currentFilter === "slow-cycles") {
29972
+ if (targetCycleTime > 0 && cycleTime > 0) {
29973
+ if (cycleTime < targetCycleTime) {
29974
+ return `\u26A1 Fast Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29975
+ } else {
29976
+ return `\u{1F40C} Slow Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29977
+ }
29978
+ } else {
29979
+ switch (currentFilter) {
29980
+ case "fast-cycles":
29981
+ return `\u26A1 Fast Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29982
+ case "slow-cycles":
29983
+ return `\u{1F40C} Slow Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29984
+ }
29985
+ }
29830
29986
  }
29987
+ if (currentFilter === "longest-idles") {
29988
+ return `\u23F0 Long Idle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29989
+ }
29990
+ return video.description || "Performance Clip";
29831
29991
  }
29832
29992
  switch (video.type) {
29833
29993
  case "low_value":
@@ -29846,7 +30006,7 @@ var BottlenecksContent = ({
29846
30006
  default:
29847
30007
  return video.description || "";
29848
30008
  }
29849
- };
30009
+ }, [workspaceTargetCycleTime]);
29850
30010
  if (!dashboardConfig?.s3Config) {
29851
30011
  return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
29852
30012
  /* @__PURE__ */ jsx(XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
@@ -30118,12 +30278,32 @@ var BottlenecksContent = ({
30118
30278
  badgeText = "Idle";
30119
30279
  badgeColor = "bg-red-100 text-red-700";
30120
30280
  } else {
30121
- if (clip.severity === "high" || clip.duration && clip.duration > 60) {
30122
- badgeText = "Slow";
30123
- badgeColor = "bg-red-100 text-red-700";
30281
+ const parseCycleTime2 = (value) => {
30282
+ if (typeof value === "number") return isNaN(value) ? null : value;
30283
+ if (typeof value === "string") {
30284
+ const parsed = parseFloat(value);
30285
+ return isNaN(parsed) ? null : parsed;
30286
+ }
30287
+ return null;
30288
+ };
30289
+ const clipCycleTime = parseCycleTime2(clip.cycle_time_seconds) ?? parseCycleTime2(clip.duration) ?? parseCycleTime2(clip.original_task_metadata?.cycle_time);
30290
+ const targetCycleTime = workspaceTargetCycleTime && workspaceTargetCycleTime > 0 ? workspaceTargetCycleTime : null;
30291
+ if (clipCycleTime && targetCycleTime) {
30292
+ if (clipCycleTime <= targetCycleTime) {
30293
+ badgeText = "Fast";
30294
+ badgeColor = "bg-green-100 text-green-700";
30295
+ } else {
30296
+ badgeText = "Slow";
30297
+ badgeColor = "bg-red-100 text-red-700";
30298
+ }
30124
30299
  } else {
30125
- badgeText = "Average";
30126
- badgeColor = "bg-yellow-100 text-yellow-700";
30300
+ if (clip.severity === "high" || clip.duration && clip.duration > 60) {
30301
+ badgeText = "Slow";
30302
+ badgeColor = "bg-red-100 text-red-700";
30303
+ } else {
30304
+ badgeText = "Average";
30305
+ badgeColor = "bg-yellow-100 text-yellow-700";
30306
+ }
30127
30307
  }
30128
30308
  }
30129
30309
  return /* @__PURE__ */ jsx(
@@ -30166,6 +30346,7 @@ var BottlenecksContent = ({
30166
30346
  workspaceId,
30167
30347
  date: date || getOperationalDate(timezone),
30168
30348
  shift: effectiveShift,
30349
+ targetCycleTime: workspaceTargetCycleTime,
30169
30350
  onFilterChange: (filterId) => {
30170
30351
  updateActiveFilter(filterId);
30171
30352
  const category = categoriesToShow.find((cat) => cat.type === filterId);
@@ -41453,7 +41634,6 @@ function HomeView({
41453
41634
  factoryName = "Simba Beer - Line 1"
41454
41635
  }) {
41455
41636
  const [isHydrated, setIsHydrated] = useState(false);
41456
- const [selectedLineId, setSelectedLineId] = useState(factoryViewId);
41457
41637
  const [isChangingFilter, setIsChangingFilter] = useState(false);
41458
41638
  const [errorMessage, setErrorMessage] = useState(null);
41459
41639
  const [displayNamesInitialized, setDisplayNamesInitialized] = useState(false);
@@ -41470,9 +41650,36 @@ function HomeView({
41470
41650
  }
41471
41651
  return [factoryViewId, ...allLineIds];
41472
41652
  }, [factoryViewId, allLineIds, isSupervisor, hasMultipleLines]);
41653
+ const LINE_FILTER_STORAGE_KEY = "optifye_home_line_filter";
41654
+ const [selectedLineId, setSelectedLineId] = useState(() => {
41655
+ if (typeof window === "undefined") {
41656
+ return factoryViewId;
41657
+ }
41658
+ try {
41659
+ const savedLineId = sessionStorage.getItem(LINE_FILTER_STORAGE_KEY);
41660
+ if (savedLineId) {
41661
+ const allAvailableIds = [factoryViewId, ...allLineIds];
41662
+ if (allAvailableIds.includes(savedLineId)) {
41663
+ return savedLineId;
41664
+ }
41665
+ }
41666
+ } catch (error) {
41667
+ console.warn("Failed to read line filter from sessionStorage:", error);
41668
+ }
41669
+ return factoryViewId;
41670
+ });
41473
41671
  useEffect(() => {
41474
41672
  if (user) {
41475
41673
  if (isSupervisor && allLineIds.length > 0) {
41674
+ try {
41675
+ const savedLineId = sessionStorage.getItem(LINE_FILTER_STORAGE_KEY);
41676
+ if (savedLineId && allLineIds.includes(savedLineId)) {
41677
+ setSelectedLineId(savedLineId);
41678
+ return;
41679
+ }
41680
+ } catch (error) {
41681
+ console.warn("Failed to read line filter from sessionStorage:", error);
41682
+ }
41476
41683
  setSelectedLineId(allLineIds[0]);
41477
41684
  }
41478
41685
  }
@@ -41507,11 +41714,12 @@ function HomeView({
41507
41714
  };
41508
41715
  initDisplayNames();
41509
41716
  }, [selectedLineId, factoryViewId, allLineIds]);
41717
+ const displayNameLineId = selectedLineId === factoryViewId ? void 0 : selectedLineId;
41510
41718
  const {
41511
41719
  displayNames: workspaceDisplayNames,
41512
41720
  loading: displayNamesLoading,
41513
41721
  error: displayNamesError
41514
- } = useWorkspaceDisplayNames(selectedLineId, void 0);
41722
+ } = useWorkspaceDisplayNames(displayNameLineId, void 0);
41515
41723
  useCallback(() => {
41516
41724
  console.log("Refetching KPIs after line metrics update");
41517
41725
  }, []);
@@ -41552,6 +41760,12 @@ function HomeView({
41552
41760
  }
41553
41761
  return allActiveBreaks.filter((breakItem) => breakItem.lineId === selectedLineId);
41554
41762
  }, [allActiveBreaks, selectedLineId, factoryViewId]);
41763
+ const [breakNotificationsDismissed, setBreakNotificationsDismissed] = useState(false);
41764
+ useEffect(() => {
41765
+ if (activeBreaks.length > 0) {
41766
+ setBreakNotificationsDismissed(false);
41767
+ }
41768
+ }, [activeBreaks.length]);
41555
41769
  const showBottleneckNotification = useCallback(async (bottleneck) => {
41556
41770
  try {
41557
41771
  console.log("\u{1F514} [Notification] Raw bottleneck data:", bottleneck);
@@ -41850,7 +42064,12 @@ function HomeView({
41850
42064
  const handleLineChange = useCallback((value) => {
41851
42065
  setIsChangingFilter(true);
41852
42066
  setSelectedLineId(value);
41853
- }, []);
42067
+ try {
42068
+ sessionStorage.setItem(LINE_FILTER_STORAGE_KEY, value);
42069
+ } catch (error) {
42070
+ console.warn("Failed to save line filter to sessionStorage:", error);
42071
+ }
42072
+ }, [LINE_FILTER_STORAGE_KEY]);
41854
42073
  useEffect(() => {
41855
42074
  if (!metricsLoading && !kpisLoading && isChangingFilter) {
41856
42075
  if (workspaceMetrics.length > 0 || selectedLineId === factoryViewId) {
@@ -41875,7 +42094,7 @@ function HomeView({
41875
42094
  /* @__PURE__ */ jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md text-xs sm:text-sm", children: availableLineIds.map((id3) => /* @__PURE__ */ jsx(SelectItem, { value: id3, children: lineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
41876
42095
  ] });
41877
42096
  }, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
41878
- const isInitialLoading = !isHydrated || displayNamesLoading || !displayNamesInitialized && displayNamesLoading;
42097
+ const isInitialLoading = !isHydrated || displayNamesLoading || !displayNamesInitialized;
41879
42098
  const isDataLoading = metricsLoading || kpisLoading;
41880
42099
  if (isInitialLoading) {
41881
42100
  return /* @__PURE__ */ jsx(LoadingPageCmp, { message: "Loading Dashboard..." });
@@ -41963,7 +42182,8 @@ function HomeView({
41963
42182
  {
41964
42183
  activeBreaks,
41965
42184
  lineNames,
41966
- isVisible: !breaksLoading && !breaksError
42185
+ isVisible: !breaksLoading && !breaksError && !breakNotificationsDismissed,
42186
+ onDismiss: () => setBreakNotificationsDismissed(true)
41967
42187
  }
41968
42188
  ),
41969
42189
  /* @__PURE__ */ jsx(