@optifye/dashboard-core 6.9.6 → 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.d.mts CHANGED
@@ -6575,6 +6575,7 @@ interface FileManagerFiltersProps {
6575
6575
  date?: string;
6576
6576
  shift?: string | number;
6577
6577
  className?: string;
6578
+ targetCycleTime?: number | null;
6578
6579
  }
6579
6580
  declare const FileManagerFilters: React__default.FC<FileManagerFiltersProps>;
6580
6581
 
package/dist/index.d.ts CHANGED
@@ -6575,6 +6575,7 @@ interface FileManagerFiltersProps {
6575
6575
  date?: string;
6576
6576
  shift?: string | number;
6577
6577
  className?: string;
6578
+ targetCycleTime?: number | null;
6578
6579
  }
6579
6580
  declare const FileManagerFilters: React__default.FC<FileManagerFiltersProps>;
6580
6581
 
package/dist/index.js CHANGED
@@ -25662,18 +25662,30 @@ var BreakNotificationPopup = ({
25662
25662
  className = "",
25663
25663
  lineNames = {}
25664
25664
  }) => {
25665
- const [isDismissed, setIsDismissed] = React23.useState(false);
25665
+ const [currentIndex, setCurrentIndex] = React23.useState(0);
25666
+ const [visibleBreaks, setVisibleBreaks] = React23.useState(activeBreaks);
25666
25667
  const [currentTime, setCurrentTime] = React23.useState(/* @__PURE__ */ new Date());
25668
+ React23.useEffect(() => {
25669
+ setVisibleBreaks(activeBreaks);
25670
+ if (currentIndex >= activeBreaks.length) {
25671
+ setCurrentIndex(Math.max(0, activeBreaks.length - 1));
25672
+ }
25673
+ }, [activeBreaks, currentIndex]);
25667
25674
  React23.useEffect(() => {
25668
25675
  const timer = setInterval(() => {
25669
25676
  setCurrentTime(/* @__PURE__ */ new Date());
25670
25677
  }, 6e4);
25671
25678
  return () => clearInterval(timer);
25672
25679
  }, []);
25673
- const handleDismiss = () => {
25674
- setIsDismissed(true);
25680
+ const handleDismissAll = () => {
25675
25681
  onDismiss?.();
25676
25682
  };
25683
+ const handleNext = () => {
25684
+ setCurrentIndex((prev) => (prev + 1) % visibleBreaks.length);
25685
+ };
25686
+ const handlePrevious = () => {
25687
+ setCurrentIndex((prev) => (prev - 1 + visibleBreaks.length) % visibleBreaks.length);
25688
+ };
25677
25689
  const formatTime3 = (minutes) => {
25678
25690
  const hours = Math.floor(minutes / 60);
25679
25691
  const mins = minutes % 60;
@@ -25682,69 +25694,130 @@ var BreakNotificationPopup = ({
25682
25694
  }
25683
25695
  return `${mins}m`;
25684
25696
  };
25685
- if (!isVisible || isDismissed || activeBreaks.length === 0) {
25697
+ const formatTo12Hour = (time24) => {
25698
+ const [hours, minutes] = time24.split(":").map(Number);
25699
+ if (isNaN(hours) || isNaN(minutes)) {
25700
+ return time24;
25701
+ }
25702
+ const period = hours >= 12 ? "PM" : "AM";
25703
+ const hours12 = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
25704
+ return `${hours12}:${minutes.toString().padStart(2, "0")} ${period}`;
25705
+ };
25706
+ if (!isVisible || visibleBreaks.length === 0) {
25686
25707
  return null;
25687
25708
  }
25688
- return /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: activeBreaks.map((breakItem, index) => /* @__PURE__ */ jsxRuntime.jsx(
25689
- motion.div,
25690
- {
25691
- initial: { opacity: 0, x: 100, y: -20 },
25692
- animate: { opacity: 1, x: 0, y: 0 },
25693
- exit: { opacity: 0, x: 100, y: -20 },
25694
- transition: { duration: 0.3, ease: "easeOut", delay: index * 0.1 },
25695
- className: `fixed right-4 z-50 max-w-xs w-full ${className}`,
25696
- style: { top: `${6 + index * 12}rem` },
25697
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-lg overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
25698
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
25699
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(AxelOrb, { size: "md" }) }),
25700
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
25701
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
25702
- /* @__PURE__ */ jsxRuntime.jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
25703
- breakItem.remarks || "Break",
25704
- (activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500 ml-1", children: [
25705
- "\u2022 ",
25706
- lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}`
25707
- ] })
25709
+ const currentBreak = visibleBreaks[currentIndex];
25710
+ return /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `fixed right-4 top-24 z-50 max-w-xs w-full ${className}`, children: [
25711
+ visibleBreaks.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
25712
+ /* @__PURE__ */ jsxRuntime.jsx(
25713
+ "div",
25714
+ {
25715
+ className: "absolute inset-0 bg-white rounded-lg border border-gray-300 shadow-lg",
25716
+ style: {
25717
+ transform: "translateY(12px) scale(0.94)",
25718
+ opacity: 0.6,
25719
+ zIndex: -2
25720
+ }
25721
+ }
25722
+ ),
25723
+ visibleBreaks.length > 2 && /* @__PURE__ */ jsxRuntime.jsx(
25724
+ "div",
25725
+ {
25726
+ className: "absolute inset-0 bg-white rounded-lg border border-gray-200 shadow-md",
25727
+ style: {
25728
+ transform: "translateY(6px) scale(0.97)",
25729
+ opacity: 0.8,
25730
+ zIndex: -1
25731
+ }
25732
+ }
25733
+ )
25734
+ ] }),
25735
+ /* @__PURE__ */ jsxRuntime.jsx(
25736
+ motion.div,
25737
+ {
25738
+ initial: { opacity: 0, x: 100, y: -20 },
25739
+ animate: { opacity: 1, x: 0, y: 0 },
25740
+ exit: { opacity: 0, x: -100, y: -20 },
25741
+ transition: { duration: 0.3, ease: "easeOut" },
25742
+ className: "relative z-10",
25743
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-xl overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
25744
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
25745
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(AxelOrb, { size: "md" }) }),
25746
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
25747
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
25748
+ /* @__PURE__ */ jsxRuntime.jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
25749
+ currentBreak.remarks || "Break",
25750
+ lineNames[currentBreak.lineId] && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500 ml-1", children: [
25751
+ "\u2022 ",
25752
+ lineNames[currentBreak.lineId]
25753
+ ] })
25754
+ ] }),
25755
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Coffee, { className: "w-4 h-4 text-amber-500" })
25708
25756
  ] }),
25709
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Coffee, { className: "w-4 h-4 text-amber-500" })
25710
- ] }),
25711
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600 leading-relaxed mb-2", children: [
25712
- "Currently active from ",
25713
- breakItem.startTime,
25714
- " to ",
25715
- breakItem.endTime,
25716
- ". ",
25717
- formatTime3(breakItem.elapsedMinutes),
25718
- " elapsed of ",
25719
- formatTime3(breakItem.duration),
25720
- " total."
25721
- ] }),
25722
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(
25723
- "div",
25724
- {
25725
- className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
25726
- style: {
25727
- width: `${Math.min(100, Math.max(0, breakItem.elapsedMinutes / breakItem.duration * 100))}%`
25757
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600 mb-1", children: [
25758
+ formatTo12Hour(currentBreak.startTime),
25759
+ " - ",
25760
+ formatTo12Hour(currentBreak.endTime)
25761
+ ] }),
25762
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500 mb-2", children: [
25763
+ formatTime3(currentBreak.elapsedMinutes),
25764
+ " elapsed of ",
25765
+ formatTime3(currentBreak.duration),
25766
+ " total"
25767
+ ] }),
25768
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(
25769
+ "div",
25770
+ {
25771
+ className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
25772
+ style: {
25773
+ width: `${Math.min(100, Math.max(0, currentBreak.elapsedMinutes / currentBreak.duration * 100))}%`
25774
+ }
25728
25775
  }
25729
- }
25730
- ) }) })
25731
- ] })
25732
- ] }),
25733
- /* @__PURE__ */ jsxRuntime.jsx(
25734
- "button",
25735
- {
25736
- onClick: handleDismiss,
25737
- onTouchStart: () => {
25738
- },
25739
- 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",
25740
- "aria-label": "Dismiss notification",
25741
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
25742
- }
25743
- )
25744
- ] }) }) })
25745
- },
25746
- `${breakItem.lineId}-${breakItem.startTime}-${index}`
25747
- )) });
25776
+ ) }),
25777
+ visibleBreaks.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 pt-2 border-t border-gray-100", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
25778
+ /* @__PURE__ */ jsxRuntime.jsx(
25779
+ "button",
25780
+ {
25781
+ onClick: handlePrevious,
25782
+ className: "p-1 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-full transition-colors",
25783
+ "aria-label": "Previous break",
25784
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "w-4 h-4" })
25785
+ }
25786
+ ),
25787
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-500", children: [
25788
+ currentIndex + 1,
25789
+ " of ",
25790
+ visibleBreaks.length,
25791
+ " breaks"
25792
+ ] }),
25793
+ /* @__PURE__ */ jsxRuntime.jsx(
25794
+ "button",
25795
+ {
25796
+ onClick: handleNext,
25797
+ className: "p-1 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-full transition-colors",
25798
+ "aria-label": "Next break",
25799
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-4 h-4" })
25800
+ }
25801
+ )
25802
+ ] }) })
25803
+ ] })
25804
+ ] }),
25805
+ /* @__PURE__ */ jsxRuntime.jsx(
25806
+ "button",
25807
+ {
25808
+ onClick: handleDismissAll,
25809
+ onTouchStart: () => {
25810
+ },
25811
+ 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",
25812
+ "aria-label": "Dismiss all breaks",
25813
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
25814
+ }
25815
+ )
25816
+ ] }) }) })
25817
+ },
25818
+ currentIndex
25819
+ )
25820
+ ] }) });
25748
25821
  };
25749
25822
  async function fetchAxelNotifications() {
25750
25823
  try {
@@ -26708,8 +26781,11 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
26708
26781
  experimentalLLHLS: false,
26709
26782
  // Disable Low Latency HLS for VOD
26710
26783
  // Connection settings
26711
- enableWorker: true,
26712
- // Use web worker for better performance
26784
+ // Chrome 130+ started throwing "Cannot perform Construct on a detached ArrayBuffer"
26785
+ // whenever the transmux worker tried to rehydrate transferred buffers that originated
26786
+ // from Blob-based playlists. Disabling the worker keeps playback stable at the cost
26787
+ // of slightly higher main-thread usage, which is acceptable for the dashboard usage.
26788
+ enableWorker: false,
26713
26789
  progressive: true,
26714
26790
  // Progressive download
26715
26791
  // Adaptive bitrate settings (if multi-quality available)
@@ -27719,10 +27795,30 @@ function useWorkspaceCrop(workspaceId) {
27719
27795
  }, [workspaceId]);
27720
27796
  return { crop, isLoading, error };
27721
27797
  }
27722
- var getSeverityIcon = (severity, categoryId) => {
27798
+ var parseCycleTime = (value) => {
27799
+ if (typeof value === "number" && Number.isFinite(value)) {
27800
+ return value;
27801
+ }
27802
+ if (typeof value === "string") {
27803
+ const parsed = Number(value);
27804
+ return Number.isFinite(parsed) ? parsed : null;
27805
+ }
27806
+ return null;
27807
+ };
27808
+ var extractCycleTimeSeconds = (clip) => {
27809
+ return parseCycleTime(clip?.cycleTimeSeconds) ?? parseCycleTime(clip?.cycle_time_seconds) ?? parseCycleTime(clip?.duration) ?? parseCycleTime(clip?.original_task_metadata?.cycle_time) ?? null;
27810
+ };
27811
+ var getSeverityIcon = (severity, categoryId, cycleTimeSeconds, targetCycleTime) => {
27723
27812
  if (categoryId === "idle_time" || categoryId === "low_value" || categoryId === "longest-idles") {
27724
27813
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-red-500" });
27725
27814
  }
27815
+ if (categoryId === "cycle_completion" && targetCycleTime && targetCycleTime > 0 && cycleTimeSeconds !== null && cycleTimeSeconds !== void 0) {
27816
+ if (cycleTimeSeconds <= targetCycleTime) {
27817
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "h-3 w-3 text-green-500" });
27818
+ } else {
27819
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-red-500" });
27820
+ }
27821
+ }
27726
27822
  switch (severity) {
27727
27823
  case "high":
27728
27824
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-red-500" });
@@ -27777,7 +27873,8 @@ var FileManagerFilters = ({
27777
27873
  workspaceId,
27778
27874
  date,
27779
27875
  shift,
27780
- className = ""
27876
+ className = "",
27877
+ targetCycleTime = null
27781
27878
  }) => {
27782
27879
  const [expandedNodes, setExpandedNodes] = React23.useState(/* @__PURE__ */ new Set());
27783
27880
  const [startTime, setStartTime] = React23.useState("");
@@ -27797,6 +27894,26 @@ var FileManagerFilters = ({
27797
27894
  const [percentileCounts, setPercentileCounts] = React23.useState({});
27798
27895
  const [percentileClips, setPercentileClips] = React23.useState({});
27799
27896
  const [loadingPercentile, setLoadingPercentile] = React23.useState(false);
27897
+ const resolvedTargetCycleTime = targetCycleTime && targetCycleTime > 0 ? targetCycleTime : null;
27898
+ const getClipBadge = React23.useCallback((node) => {
27899
+ if (node.categoryId === "idle_time" || node.categoryId === "low_value") {
27900
+ return { text: "Idle", className: "bg-red-100 text-red-700" };
27901
+ }
27902
+ if (node.categoryId === "cycle_completion" && resolvedTargetCycleTime && node.cycleTimeSeconds !== void 0 && node.cycleTimeSeconds !== null) {
27903
+ const isFast = node.cycleTimeSeconds <= resolvedTargetCycleTime;
27904
+ return {
27905
+ text: isFast ? "Fast" : "Slow",
27906
+ className: isFast ? "bg-green-100 text-green-700" : "bg-red-100 text-red-700"
27907
+ };
27908
+ }
27909
+ if (node.severity === "high") {
27910
+ return { text: "Slow", className: "bg-red-100 text-red-700" };
27911
+ }
27912
+ if (node.severity === "medium") {
27913
+ return { text: "Average", className: "bg-yellow-100 text-yellow-700" };
27914
+ }
27915
+ return { text: "Fast", className: "bg-green-100 text-green-700" };
27916
+ }, [resolvedTargetCycleTime]);
27800
27917
  const fetchClipMetadata = React23.useCallback(async (categoryId, page = 1) => {
27801
27918
  if (!workspaceId || !date || shift === void 0) {
27802
27919
  console.warn("[FileManager] Missing required params for clip metadata fetch");
@@ -28034,18 +28151,20 @@ var FileManagerFilters = ({
28034
28151
  timeZone: timezone
28035
28152
  // Use database timezone for display
28036
28153
  });
28154
+ const cycleTime = extractCycleTimeSeconds(clip);
28037
28155
  return {
28038
28156
  id: clip.id,
28039
28157
  label: `${timeString}${clip.duration && category.id !== "idle_time" ? ` - (${clip.duration.toFixed(1)}s)` : ""}`,
28040
28158
  type: "video",
28041
- icon: getSeverityIcon(clip.severity, category.id),
28159
+ icon: getSeverityIcon(clip.severity, category.id, cycleTime, resolvedTargetCycleTime),
28042
28160
  timestamp: clip.clip_timestamp,
28043
28161
  severity: clip.severity,
28044
28162
  clipId: clip.clipId,
28045
28163
  // Store stable UUID for navigation
28046
28164
  categoryId: category.id,
28047
- clipPosition: index + 1
28165
+ clipPosition: index + 1,
28048
28166
  // Store 1-based position
28167
+ cycleTimeSeconds: cycleTime
28049
28168
  };
28050
28169
  });
28051
28170
  regularCategoryNodes.push({
@@ -28084,15 +28203,17 @@ var FileManagerFilters = ({
28084
28203
  minute: "2-digit",
28085
28204
  timeZone: timezone
28086
28205
  });
28206
+ const cycleTime = extractCycleTimeSeconds(clip);
28087
28207
  return {
28088
28208
  id: clip.id,
28089
28209
  label: `${timeString}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28090
28210
  type: "video",
28091
- icon: getSeverityIcon(clip.severity, "fast-cycles"),
28211
+ icon: getSeverityIcon(clip.severity, "fast-cycles", cycleTime, resolvedTargetCycleTime),
28092
28212
  timestamp: clip.creation_timestamp,
28093
28213
  severity: clip.severity,
28094
28214
  clipId: clip.id,
28095
- categoryId: "fast-cycles"
28215
+ categoryId: "fast-cycles",
28216
+ cycleTimeSeconds: cycleTime
28096
28217
  };
28097
28218
  })
28098
28219
  },
@@ -28114,15 +28235,17 @@ var FileManagerFilters = ({
28114
28235
  minute: "2-digit",
28115
28236
  timeZone: timezone
28116
28237
  });
28238
+ const cycleTime = extractCycleTimeSeconds(clip);
28117
28239
  return {
28118
28240
  id: clip.id,
28119
28241
  label: `${timeString}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28120
28242
  type: "video",
28121
- icon: getSeverityIcon(clip.severity, "slow-cycles"),
28243
+ icon: getSeverityIcon(clip.severity, "slow-cycles", cycleTime, resolvedTargetCycleTime),
28122
28244
  timestamp: clip.creation_timestamp,
28123
28245
  severity: clip.severity,
28124
28246
  clipId: clip.id,
28125
- categoryId: "slow-cycles"
28247
+ categoryId: "slow-cycles",
28248
+ cycleTimeSeconds: cycleTime
28126
28249
  };
28127
28250
  })
28128
28251
  }
@@ -28243,7 +28366,10 @@ var FileManagerFilters = ({
28243
28366
  /* @__PURE__ */ jsxRuntime.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 }),
28244
28367
  node.type === "category" && categories.find((c) => c.id === node.id)?.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: categories.find((c) => c.id === node.id)?.description }),
28245
28368
  node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
28246
- node.type === "video" && node.severity && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: /* @__PURE__ */ jsxRuntime.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" }) })
28369
+ node.type === "video" && (node.severity || node.categoryId === "cycle_completion" || node.categoryId === "idle_time" || node.categoryId === "low_value") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: (() => {
28370
+ const badge = getClipBadge(node);
28371
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${badge.className}`, children: badge.text });
28372
+ })() })
28247
28373
  ] }),
28248
28374
  node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsxRuntime.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 }) })
28249
28375
  ] })
@@ -28910,6 +29036,19 @@ var BottlenecksContent = ({
28910
29036
  }
28911
29037
  }, [shift, date, timezone, shiftConfig, workspaceId]);
28912
29038
  const { crop: workspaceCrop} = useWorkspaceCrop(workspaceId);
29039
+ const { metrics: workspaceMetrics } = useWorkspaceDetailedMetrics(
29040
+ workspaceId,
29041
+ date,
29042
+ shift !== void 0 && shift !== null ? Number(shift) : void 0
29043
+ );
29044
+ const workspaceTargetCycleTimeRaw = workspaceMetrics?.ideal_cycle_time;
29045
+ const workspaceTargetCycleTime = (() => {
29046
+ if (workspaceTargetCycleTimeRaw === void 0 || workspaceTargetCycleTimeRaw === null) {
29047
+ return null;
29048
+ }
29049
+ const numericValue = typeof workspaceTargetCycleTimeRaw === "number" ? workspaceTargetCycleTimeRaw : Number(workspaceTargetCycleTimeRaw);
29050
+ return Number.isFinite(numericValue) ? numericValue : null;
29051
+ })();
28913
29052
  const videoRef = React23.useRef(null);
28914
29053
  const [initialFilter, setInitialFilter] = React23.useState("");
28915
29054
  const currentIndexRef = React23.useRef(0);
@@ -29852,21 +29991,33 @@ var BottlenecksContent = ({
29852
29991
  return () => window.removeEventListener("keydown", handleEscape);
29853
29992
  }
29854
29993
  }, [isFullscreen, exitFullscreen]);
29855
- const getClipTypeLabel = (video) => {
29994
+ const getClipTypeLabel = React23.useCallback((video) => {
29856
29995
  if (!video) return "";
29857
29996
  const currentFilter = activeFilterRef.current;
29997
+ const targetCycleTime = workspaceTargetCycleTime || 0;
29858
29998
  if (isPercentileCategory(currentFilter)) {
29859
29999
  const percentileValue = video.percentile?.toFixed(1);
29860
- switch (currentFilter) {
29861
- case "fast-cycles":
29862
- return `\u26A1 Fast Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29863
- case "slow-cycles":
29864
- return `\u{1F40C} Slow Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29865
- case "longest-idles":
29866
- return `\u23F0 Long Idle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29867
- default:
29868
- return video.description || "Performance Clip";
30000
+ const cycleTime = video.cycle_time_seconds || 0;
30001
+ if (currentFilter === "fast-cycles" || currentFilter === "slow-cycles") {
30002
+ if (targetCycleTime > 0 && cycleTime > 0) {
30003
+ if (cycleTime < targetCycleTime) {
30004
+ return `\u26A1 Fast Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
30005
+ } else {
30006
+ return `\u{1F40C} Slow Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
30007
+ }
30008
+ } else {
30009
+ switch (currentFilter) {
30010
+ case "fast-cycles":
30011
+ return `\u26A1 Fast Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
30012
+ case "slow-cycles":
30013
+ return `\u{1F40C} Slow Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
30014
+ }
30015
+ }
29869
30016
  }
30017
+ if (currentFilter === "longest-idles") {
30018
+ return `\u23F0 Long Idle ${percentileValue ? `(${percentileValue}%)` : ""}`;
30019
+ }
30020
+ return video.description || "Performance Clip";
29870
30021
  }
29871
30022
  switch (video.type) {
29872
30023
  case "low_value":
@@ -29885,7 +30036,7 @@ var BottlenecksContent = ({
29885
30036
  default:
29886
30037
  return video.description || "";
29887
30038
  }
29888
- };
30039
+ }, [workspaceTargetCycleTime]);
29889
30040
  if (!dashboardConfig?.s3Config) {
29890
30041
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
29891
30042
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
@@ -30157,12 +30308,32 @@ var BottlenecksContent = ({
30157
30308
  badgeText = "Idle";
30158
30309
  badgeColor = "bg-red-100 text-red-700";
30159
30310
  } else {
30160
- if (clip.severity === "high" || clip.duration && clip.duration > 60) {
30161
- badgeText = "Slow";
30162
- badgeColor = "bg-red-100 text-red-700";
30311
+ const parseCycleTime2 = (value) => {
30312
+ if (typeof value === "number") return isNaN(value) ? null : value;
30313
+ if (typeof value === "string") {
30314
+ const parsed = parseFloat(value);
30315
+ return isNaN(parsed) ? null : parsed;
30316
+ }
30317
+ return null;
30318
+ };
30319
+ const clipCycleTime = parseCycleTime2(clip.cycle_time_seconds) ?? parseCycleTime2(clip.duration) ?? parseCycleTime2(clip.original_task_metadata?.cycle_time);
30320
+ const targetCycleTime = workspaceTargetCycleTime && workspaceTargetCycleTime > 0 ? workspaceTargetCycleTime : null;
30321
+ if (clipCycleTime && targetCycleTime) {
30322
+ if (clipCycleTime <= targetCycleTime) {
30323
+ badgeText = "Fast";
30324
+ badgeColor = "bg-green-100 text-green-700";
30325
+ } else {
30326
+ badgeText = "Slow";
30327
+ badgeColor = "bg-red-100 text-red-700";
30328
+ }
30163
30329
  } else {
30164
- badgeText = "Average";
30165
- badgeColor = "bg-yellow-100 text-yellow-700";
30330
+ if (clip.severity === "high" || clip.duration && clip.duration > 60) {
30331
+ badgeText = "Slow";
30332
+ badgeColor = "bg-red-100 text-red-700";
30333
+ } else {
30334
+ badgeText = "Average";
30335
+ badgeColor = "bg-yellow-100 text-yellow-700";
30336
+ }
30166
30337
  }
30167
30338
  }
30168
30339
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -30205,6 +30376,7 @@ var BottlenecksContent = ({
30205
30376
  workspaceId,
30206
30377
  date: date || getOperationalDate(timezone),
30207
30378
  shift: effectiveShift,
30379
+ targetCycleTime: workspaceTargetCycleTime,
30208
30380
  onFilterChange: (filterId) => {
30209
30381
  updateActiveFilter(filterId);
30210
30382
  const category = categoriesToShow.find((cat) => cat.type === filterId);
@@ -41492,7 +41664,6 @@ function HomeView({
41492
41664
  factoryName = "Simba Beer - Line 1"
41493
41665
  }) {
41494
41666
  const [isHydrated, setIsHydrated] = React23.useState(false);
41495
- const [selectedLineId, setSelectedLineId] = React23.useState(factoryViewId);
41496
41667
  const [isChangingFilter, setIsChangingFilter] = React23.useState(false);
41497
41668
  const [errorMessage, setErrorMessage] = React23.useState(null);
41498
41669
  const [displayNamesInitialized, setDisplayNamesInitialized] = React23.useState(false);
@@ -41509,9 +41680,36 @@ function HomeView({
41509
41680
  }
41510
41681
  return [factoryViewId, ...allLineIds];
41511
41682
  }, [factoryViewId, allLineIds, isSupervisor, hasMultipleLines]);
41683
+ const LINE_FILTER_STORAGE_KEY = "optifye_home_line_filter";
41684
+ const [selectedLineId, setSelectedLineId] = React23.useState(() => {
41685
+ if (typeof window === "undefined") {
41686
+ return factoryViewId;
41687
+ }
41688
+ try {
41689
+ const savedLineId = sessionStorage.getItem(LINE_FILTER_STORAGE_KEY);
41690
+ if (savedLineId) {
41691
+ const allAvailableIds = [factoryViewId, ...allLineIds];
41692
+ if (allAvailableIds.includes(savedLineId)) {
41693
+ return savedLineId;
41694
+ }
41695
+ }
41696
+ } catch (error) {
41697
+ console.warn("Failed to read line filter from sessionStorage:", error);
41698
+ }
41699
+ return factoryViewId;
41700
+ });
41512
41701
  React23.useEffect(() => {
41513
41702
  if (user) {
41514
41703
  if (isSupervisor && allLineIds.length > 0) {
41704
+ try {
41705
+ const savedLineId = sessionStorage.getItem(LINE_FILTER_STORAGE_KEY);
41706
+ if (savedLineId && allLineIds.includes(savedLineId)) {
41707
+ setSelectedLineId(savedLineId);
41708
+ return;
41709
+ }
41710
+ } catch (error) {
41711
+ console.warn("Failed to read line filter from sessionStorage:", error);
41712
+ }
41515
41713
  setSelectedLineId(allLineIds[0]);
41516
41714
  }
41517
41715
  }
@@ -41592,6 +41790,12 @@ function HomeView({
41592
41790
  }
41593
41791
  return allActiveBreaks.filter((breakItem) => breakItem.lineId === selectedLineId);
41594
41792
  }, [allActiveBreaks, selectedLineId, factoryViewId]);
41793
+ const [breakNotificationsDismissed, setBreakNotificationsDismissed] = React23.useState(false);
41794
+ React23.useEffect(() => {
41795
+ if (activeBreaks.length > 0) {
41796
+ setBreakNotificationsDismissed(false);
41797
+ }
41798
+ }, [activeBreaks.length]);
41595
41799
  const showBottleneckNotification = React23.useCallback(async (bottleneck) => {
41596
41800
  try {
41597
41801
  console.log("\u{1F514} [Notification] Raw bottleneck data:", bottleneck);
@@ -41890,7 +42094,12 @@ function HomeView({
41890
42094
  const handleLineChange = React23.useCallback((value) => {
41891
42095
  setIsChangingFilter(true);
41892
42096
  setSelectedLineId(value);
41893
- }, []);
42097
+ try {
42098
+ sessionStorage.setItem(LINE_FILTER_STORAGE_KEY, value);
42099
+ } catch (error) {
42100
+ console.warn("Failed to save line filter to sessionStorage:", error);
42101
+ }
42102
+ }, [LINE_FILTER_STORAGE_KEY]);
41894
42103
  React23.useEffect(() => {
41895
42104
  if (!metricsLoading && !kpisLoading && isChangingFilter) {
41896
42105
  if (workspaceMetrics.length > 0 || selectedLineId === factoryViewId) {
@@ -42003,7 +42212,8 @@ function HomeView({
42003
42212
  {
42004
42213
  activeBreaks,
42005
42214
  lineNames,
42006
- isVisible: !breaksLoading && !breaksError
42215
+ isVisible: !breaksLoading && !breaksError && !breakNotificationsDismissed,
42216
+ onDismiss: () => setBreakNotificationsDismissed(true)
42007
42217
  }
42008
42218
  ),
42009
42219
  /* @__PURE__ */ jsxRuntime.jsx(
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';
@@ -25632,18 +25632,30 @@ var BreakNotificationPopup = ({
25632
25632
  className = "",
25633
25633
  lineNames = {}
25634
25634
  }) => {
25635
- const [isDismissed, setIsDismissed] = useState(false);
25635
+ const [currentIndex, setCurrentIndex] = useState(0);
25636
+ const [visibleBreaks, setVisibleBreaks] = useState(activeBreaks);
25636
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]);
25637
25644
  useEffect(() => {
25638
25645
  const timer = setInterval(() => {
25639
25646
  setCurrentTime(/* @__PURE__ */ new Date());
25640
25647
  }, 6e4);
25641
25648
  return () => clearInterval(timer);
25642
25649
  }, []);
25643
- const handleDismiss = () => {
25644
- setIsDismissed(true);
25650
+ const handleDismissAll = () => {
25645
25651
  onDismiss?.();
25646
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
+ };
25647
25659
  const formatTime3 = (minutes) => {
25648
25660
  const hours = Math.floor(minutes / 60);
25649
25661
  const mins = minutes % 60;
@@ -25652,69 +25664,130 @@ var BreakNotificationPopup = ({
25652
25664
  }
25653
25665
  return `${mins}m`;
25654
25666
  };
25655
- 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) {
25656
25677
  return null;
25657
25678
  }
25658
- return /* @__PURE__ */ jsx(AnimatePresence, { children: activeBreaks.map((breakItem, index) => /* @__PURE__ */ jsx(
25659
- motion.div,
25660
- {
25661
- initial: { opacity: 0, x: 100, y: -20 },
25662
- animate: { opacity: 1, x: 0, y: 0 },
25663
- exit: { opacity: 0, x: 100, y: -20 },
25664
- transition: { duration: 0.3, ease: "easeOut", delay: index * 0.1 },
25665
- className: `fixed right-4 z-50 max-w-xs w-full ${className}`,
25666
- style: { top: `${6 + index * 12}rem` },
25667
- 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: [
25668
- /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
25669
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(AxelOrb, { size: "md" }) }),
25670
- /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
25671
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
25672
- /* @__PURE__ */ jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
25673
- breakItem.remarks || "Break",
25674
- (activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-500 ml-1", children: [
25675
- "\u2022 ",
25676
- lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}`
25677
- ] })
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" })
25678
25726
  ] }),
25679
- /* @__PURE__ */ jsx(Coffee, { className: "w-4 h-4 text-amber-500" })
25680
- ] }),
25681
- /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600 leading-relaxed mb-2", children: [
25682
- "Currently active from ",
25683
- breakItem.startTime,
25684
- " to ",
25685
- breakItem.endTime,
25686
- ". ",
25687
- formatTime3(breakItem.elapsedMinutes),
25688
- " elapsed of ",
25689
- formatTime3(breakItem.duration),
25690
- " total."
25691
- ] }),
25692
- /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsx(
25693
- "div",
25694
- {
25695
- className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
25696
- style: {
25697
- 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
+ }
25698
25745
  }
25699
- }
25700
- ) }) })
25701
- ] })
25702
- ] }),
25703
- /* @__PURE__ */ jsx(
25704
- "button",
25705
- {
25706
- onClick: handleDismiss,
25707
- onTouchStart: () => {
25708
- },
25709
- 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",
25710
- "aria-label": "Dismiss notification",
25711
- children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
25712
- }
25713
- )
25714
- ] }) }) })
25715
- },
25716
- `${breakItem.lineId}-${breakItem.startTime}-${index}`
25717
- )) });
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
+ ] }) });
25718
25791
  };
25719
25792
  async function fetchAxelNotifications() {
25720
25793
  try {
@@ -26678,8 +26751,11 @@ var VideoPlayer = React23__default.forwardRef(({
26678
26751
  experimentalLLHLS: false,
26679
26752
  // Disable Low Latency HLS for VOD
26680
26753
  // Connection settings
26681
- enableWorker: true,
26682
- // 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,
26683
26759
  progressive: true,
26684
26760
  // Progressive download
26685
26761
  // Adaptive bitrate settings (if multi-quality available)
@@ -27689,10 +27765,30 @@ function useWorkspaceCrop(workspaceId) {
27689
27765
  }, [workspaceId]);
27690
27766
  return { crop, isLoading, error };
27691
27767
  }
27692
- 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) => {
27693
27782
  if (categoryId === "idle_time" || categoryId === "low_value" || categoryId === "longest-idles") {
27694
27783
  return /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 text-red-500" });
27695
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
+ }
27696
27792
  switch (severity) {
27697
27793
  case "high":
27698
27794
  return /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3 w-3 text-red-500" });
@@ -27747,7 +27843,8 @@ var FileManagerFilters = ({
27747
27843
  workspaceId,
27748
27844
  date,
27749
27845
  shift,
27750
- className = ""
27846
+ className = "",
27847
+ targetCycleTime = null
27751
27848
  }) => {
27752
27849
  const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
27753
27850
  const [startTime, setStartTime] = useState("");
@@ -27767,6 +27864,26 @@ var FileManagerFilters = ({
27767
27864
  const [percentileCounts, setPercentileCounts] = useState({});
27768
27865
  const [percentileClips, setPercentileClips] = useState({});
27769
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]);
27770
27887
  const fetchClipMetadata = useCallback(async (categoryId, page = 1) => {
27771
27888
  if (!workspaceId || !date || shift === void 0) {
27772
27889
  console.warn("[FileManager] Missing required params for clip metadata fetch");
@@ -28004,18 +28121,20 @@ var FileManagerFilters = ({
28004
28121
  timeZone: timezone
28005
28122
  // Use database timezone for display
28006
28123
  });
28124
+ const cycleTime = extractCycleTimeSeconds(clip);
28007
28125
  return {
28008
28126
  id: clip.id,
28009
28127
  label: `${timeString}${clip.duration && category.id !== "idle_time" ? ` - (${clip.duration.toFixed(1)}s)` : ""}`,
28010
28128
  type: "video",
28011
- icon: getSeverityIcon(clip.severity, category.id),
28129
+ icon: getSeverityIcon(clip.severity, category.id, cycleTime, resolvedTargetCycleTime),
28012
28130
  timestamp: clip.clip_timestamp,
28013
28131
  severity: clip.severity,
28014
28132
  clipId: clip.clipId,
28015
28133
  // Store stable UUID for navigation
28016
28134
  categoryId: category.id,
28017
- clipPosition: index + 1
28135
+ clipPosition: index + 1,
28018
28136
  // Store 1-based position
28137
+ cycleTimeSeconds: cycleTime
28019
28138
  };
28020
28139
  });
28021
28140
  regularCategoryNodes.push({
@@ -28054,15 +28173,17 @@ var FileManagerFilters = ({
28054
28173
  minute: "2-digit",
28055
28174
  timeZone: timezone
28056
28175
  });
28176
+ const cycleTime = extractCycleTimeSeconds(clip);
28057
28177
  return {
28058
28178
  id: clip.id,
28059
28179
  label: `${timeString}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28060
28180
  type: "video",
28061
- icon: getSeverityIcon(clip.severity, "fast-cycles"),
28181
+ icon: getSeverityIcon(clip.severity, "fast-cycles", cycleTime, resolvedTargetCycleTime),
28062
28182
  timestamp: clip.creation_timestamp,
28063
28183
  severity: clip.severity,
28064
28184
  clipId: clip.id,
28065
- categoryId: "fast-cycles"
28185
+ categoryId: "fast-cycles",
28186
+ cycleTimeSeconds: cycleTime
28066
28187
  };
28067
28188
  })
28068
28189
  },
@@ -28084,15 +28205,17 @@ var FileManagerFilters = ({
28084
28205
  minute: "2-digit",
28085
28206
  timeZone: timezone
28086
28207
  });
28208
+ const cycleTime = extractCycleTimeSeconds(clip);
28087
28209
  return {
28088
28210
  id: clip.id,
28089
28211
  label: `${timeString}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28090
28212
  type: "video",
28091
- icon: getSeverityIcon(clip.severity, "slow-cycles"),
28213
+ icon: getSeverityIcon(clip.severity, "slow-cycles", cycleTime, resolvedTargetCycleTime),
28092
28214
  timestamp: clip.creation_timestamp,
28093
28215
  severity: clip.severity,
28094
28216
  clipId: clip.id,
28095
- categoryId: "slow-cycles"
28217
+ categoryId: "slow-cycles",
28218
+ cycleTimeSeconds: cycleTime
28096
28219
  };
28097
28220
  })
28098
28221
  }
@@ -28213,7 +28336,10 @@ var FileManagerFilters = ({
28213
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 }),
28214
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 }),
28215
28338
  node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
28216
- 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
+ })() })
28217
28343
  ] }),
28218
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 }) })
28219
28345
  ] })
@@ -28880,6 +29006,19 @@ var BottlenecksContent = ({
28880
29006
  }
28881
29007
  }, [shift, date, timezone, shiftConfig, workspaceId]);
28882
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
+ })();
28883
29022
  const videoRef = useRef(null);
28884
29023
  const [initialFilter, setInitialFilter] = useState("");
28885
29024
  const currentIndexRef = useRef(0);
@@ -29822,21 +29961,33 @@ var BottlenecksContent = ({
29822
29961
  return () => window.removeEventListener("keydown", handleEscape);
29823
29962
  }
29824
29963
  }, [isFullscreen, exitFullscreen]);
29825
- const getClipTypeLabel = (video) => {
29964
+ const getClipTypeLabel = useCallback((video) => {
29826
29965
  if (!video) return "";
29827
29966
  const currentFilter = activeFilterRef.current;
29967
+ const targetCycleTime = workspaceTargetCycleTime || 0;
29828
29968
  if (isPercentileCategory(currentFilter)) {
29829
29969
  const percentileValue = video.percentile?.toFixed(1);
29830
- switch (currentFilter) {
29831
- case "fast-cycles":
29832
- return `\u26A1 Fast Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29833
- case "slow-cycles":
29834
- return `\u{1F40C} Slow Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29835
- case "longest-idles":
29836
- return `\u23F0 Long Idle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29837
- default:
29838
- 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
+ }
29839
29986
  }
29987
+ if (currentFilter === "longest-idles") {
29988
+ return `\u23F0 Long Idle ${percentileValue ? `(${percentileValue}%)` : ""}`;
29989
+ }
29990
+ return video.description || "Performance Clip";
29840
29991
  }
29841
29992
  switch (video.type) {
29842
29993
  case "low_value":
@@ -29855,7 +30006,7 @@ var BottlenecksContent = ({
29855
30006
  default:
29856
30007
  return video.description || "";
29857
30008
  }
29858
- };
30009
+ }, [workspaceTargetCycleTime]);
29859
30010
  if (!dashboardConfig?.s3Config) {
29860
30011
  return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
29861
30012
  /* @__PURE__ */ jsx(XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
@@ -30127,12 +30278,32 @@ var BottlenecksContent = ({
30127
30278
  badgeText = "Idle";
30128
30279
  badgeColor = "bg-red-100 text-red-700";
30129
30280
  } else {
30130
- if (clip.severity === "high" || clip.duration && clip.duration > 60) {
30131
- badgeText = "Slow";
30132
- 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
+ }
30133
30299
  } else {
30134
- badgeText = "Average";
30135
- 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
+ }
30136
30307
  }
30137
30308
  }
30138
30309
  return /* @__PURE__ */ jsx(
@@ -30175,6 +30346,7 @@ var BottlenecksContent = ({
30175
30346
  workspaceId,
30176
30347
  date: date || getOperationalDate(timezone),
30177
30348
  shift: effectiveShift,
30349
+ targetCycleTime: workspaceTargetCycleTime,
30178
30350
  onFilterChange: (filterId) => {
30179
30351
  updateActiveFilter(filterId);
30180
30352
  const category = categoriesToShow.find((cat) => cat.type === filterId);
@@ -41462,7 +41634,6 @@ function HomeView({
41462
41634
  factoryName = "Simba Beer - Line 1"
41463
41635
  }) {
41464
41636
  const [isHydrated, setIsHydrated] = useState(false);
41465
- const [selectedLineId, setSelectedLineId] = useState(factoryViewId);
41466
41637
  const [isChangingFilter, setIsChangingFilter] = useState(false);
41467
41638
  const [errorMessage, setErrorMessage] = useState(null);
41468
41639
  const [displayNamesInitialized, setDisplayNamesInitialized] = useState(false);
@@ -41479,9 +41650,36 @@ function HomeView({
41479
41650
  }
41480
41651
  return [factoryViewId, ...allLineIds];
41481
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
+ });
41482
41671
  useEffect(() => {
41483
41672
  if (user) {
41484
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
+ }
41485
41683
  setSelectedLineId(allLineIds[0]);
41486
41684
  }
41487
41685
  }
@@ -41562,6 +41760,12 @@ function HomeView({
41562
41760
  }
41563
41761
  return allActiveBreaks.filter((breakItem) => breakItem.lineId === selectedLineId);
41564
41762
  }, [allActiveBreaks, selectedLineId, factoryViewId]);
41763
+ const [breakNotificationsDismissed, setBreakNotificationsDismissed] = useState(false);
41764
+ useEffect(() => {
41765
+ if (activeBreaks.length > 0) {
41766
+ setBreakNotificationsDismissed(false);
41767
+ }
41768
+ }, [activeBreaks.length]);
41565
41769
  const showBottleneckNotification = useCallback(async (bottleneck) => {
41566
41770
  try {
41567
41771
  console.log("\u{1F514} [Notification] Raw bottleneck data:", bottleneck);
@@ -41860,7 +42064,12 @@ function HomeView({
41860
42064
  const handleLineChange = useCallback((value) => {
41861
42065
  setIsChangingFilter(true);
41862
42066
  setSelectedLineId(value);
41863
- }, []);
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]);
41864
42073
  useEffect(() => {
41865
42074
  if (!metricsLoading && !kpisLoading && isChangingFilter) {
41866
42075
  if (workspaceMetrics.length > 0 || selectedLineId === factoryViewId) {
@@ -41973,7 +42182,8 @@ function HomeView({
41973
42182
  {
41974
42183
  activeBreaks,
41975
42184
  lineNames,
41976
- isVisible: !breaksLoading && !breaksError
42185
+ isVisible: !breaksLoading && !breaksError && !breakNotificationsDismissed,
42186
+ onDismiss: () => setBreakNotificationsDismissed(true)
41977
42187
  }
41978
42188
  ),
41979
42189
  /* @__PURE__ */ jsx(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.9.6",
3
+ "version": "6.9.7",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",