@optifye/dashboard-core 6.6.10 → 6.6.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4044,12 +4044,8 @@ var S3ClipsSupabaseService = class {
4044
4044
  id: clip.clip_id,
4045
4045
  src: clip.playlist,
4046
4046
  // Raw playlist content
4047
- timestamp: new Date(clip.date).toLocaleTimeString("en-US", {
4048
- hour12: false,
4049
- hour: "2-digit",
4050
- minute: "2-digit",
4051
- second: "2-digit"
4052
- }),
4047
+ timestamp: clip.timestamp,
4048
+ // Use pre-formatted timestamp from API (already in 12-hour format with seconds)
4053
4049
  severity: this.getSeverityFromClipType(clip.clip_type_name),
4054
4050
  description: this.getDescriptionFromClipType(clip.clip_type_name),
4055
4051
  type: clip.clip_type_name,
@@ -6545,6 +6541,10 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6545
6541
  const lineIdRef = React21.useRef(lineId);
6546
6542
  const isFetchingRef = React21.useRef(false);
6547
6543
  const updateQueueRef = React21.useRef(false);
6544
+ const onLineMetricsUpdateRef = React21.useRef(onLineMetricsUpdate);
6545
+ React21.useEffect(() => {
6546
+ onLineMetricsUpdateRef.current = onLineMetricsUpdate;
6547
+ }, [onLineMetricsUpdate]);
6548
6548
  const companySpecificMetricsTable = React21.useMemo(
6549
6549
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
6550
6550
  [entityConfig.companyId]
@@ -6660,13 +6660,17 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6660
6660
  defaultTimezone,
6661
6661
  shiftConfig
6662
6662
  ]);
6663
+ const fetchAllMetricsRef = React21.useRef(fetchAllMetrics);
6664
+ React21.useEffect(() => {
6665
+ fetchAllMetricsRef.current = fetchAllMetrics;
6666
+ }, [fetchAllMetrics]);
6663
6667
  const queueUpdate = React21.useCallback(() => {
6664
6668
  if (updateQueueRef.current || !supabase) {
6665
6669
  return;
6666
6670
  }
6667
6671
  updateQueueRef.current = true;
6668
- fetchAllMetrics();
6669
- }, [fetchAllMetrics, supabase]);
6672
+ fetchAllMetricsRef.current();
6673
+ }, [supabase]);
6670
6674
  React21.useEffect(() => {
6671
6675
  if (lineId && supabase) {
6672
6676
  fetchAllMetrics();
@@ -6681,11 +6685,11 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6681
6685
  const operationalDateForSubscription = getOperationalDate(defaultTimezone);
6682
6686
  const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
6683
6687
  if (targetLineIds.length === 0) return;
6684
- const wsMetricsFilter = `date=eq.${operationalDateForSubscription}&shift_id=eq.${currentShiftDetails.shiftId}&line_id=in.(${targetLineIds.join(",")})`;
6685
- const lineMetricsFilter = `date=eq.${operationalDateForSubscription}&shift_id=eq.${currentShiftDetails.shiftId}&line_id=in.(${targetLineIds.join(",")})`;
6688
+ const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
6689
+ const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
6686
6690
  const channels = [];
6687
6691
  const createSubscription = (table, filter2, channelNameBase, callback) => {
6688
- const channelName = `${channelNameBase}-${currentLineIdToUse}-${operationalDateForSubscription}-${currentShiftDetails.shiftId}`.replace(/[^a-zA-Z0-9_-]/g, "");
6692
+ const channelName = `${channelNameBase}-${Date.now()}`.replace(/[^a-zA-Z0-9_-]/g, "");
6689
6693
  const channel = supabase.channel(channelName).on(
6690
6694
  "postgres_changes",
6691
6695
  { event: "*", schema, table, filter: filter2 },
@@ -6695,40 +6699,29 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
6695
6699
  callback();
6696
6700
  }
6697
6701
  }
6698
- ).subscribe((status, err) => {
6699
- if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR && err) {
6700
- console.error(`[useDashboardMetrics] Subscription error for ${table} on ${currentLineIdToUse}:`, err.message);
6701
- } else if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
6702
- console.warn(`[useDashboardMetrics] Subscription timeout for ${table} on ${currentLineIdToUse}.`);
6703
- }
6704
- });
6702
+ ).subscribe();
6705
6703
  channels.push(channel);
6706
6704
  };
6707
6705
  createSubscription(companySpecificMetricsTable, wsMetricsFilter, "dashboard-ws-metrics", queueUpdate);
6708
6706
  createSubscription(configuredLineMetricsTable, lineMetricsFilter, "dashboard-line-metrics", () => {
6709
6707
  queueUpdate();
6710
- onLineMetricsUpdate?.();
6708
+ onLineMetricsUpdateRef.current?.();
6711
6709
  });
6712
6710
  return () => {
6713
6711
  channels.forEach((channel) => {
6714
- supabase?.removeChannel(channel).catch((err) => console.error("[useDashboardMetrics] Error removing channel:", err.message));
6712
+ supabase?.removeChannel(channel);
6715
6713
  });
6716
6714
  };
6717
6715
  }, [
6718
6716
  supabase,
6719
- // fetchAllMetrics, // fetchAllMetrics is now a dependency of queueUpdate
6720
6717
  queueUpdate,
6721
- // Section 6: Add queueUpdate as dependency
6722
6718
  companySpecificMetricsTable,
6723
6719
  configuredLineMetricsTable,
6724
6720
  schema,
6725
- entityConfig,
6726
- // For companyId, factoryViewId, default/secondaryLineId
6721
+ entityConfig?.companyId,
6722
+ entityConfig?.factoryViewId,
6727
6723
  defaultTimezone,
6728
- shiftConfig,
6729
- onLineMetricsUpdate,
6730
6724
  lineId
6731
- // Add lineId from props to re-run effect if it changes, managed by lineIdRef inside effect
6732
6725
  ]);
6733
6726
  return {
6734
6727
  workspaceMetrics: metrics2?.workspaceMetrics || [],
@@ -6889,6 +6882,7 @@ var useRealtimeLineMetrics = ({
6889
6882
  const updateQueueRef = React21.useRef(false);
6890
6883
  const isFetchingRef = React21.useRef(false);
6891
6884
  const channelsRef = React21.useRef([]);
6885
+ const fetchTimeoutRef = React21.useRef(null);
6892
6886
  const currentShift = React21.useMemo(() => getCurrentShift(dateTimeConfig.defaultTimezone || "Asia/Kolkata", shiftConfig), [dateTimeConfig.defaultTimezone, shiftConfig]);
6893
6887
  const shiftId = React21.useMemo(
6894
6888
  () => urlShiftId !== void 0 ? urlShiftId : currentShift.shiftId,
@@ -7098,9 +7092,17 @@ var useRealtimeLineMetrics = ({
7098
7092
  }
7099
7093
  }, [supabase, date, shiftId, urlShiftId, onMetricsUpdate, entityConfig, dateTimeConfig.defaultTimezone]);
7100
7094
  const queueUpdate = React21.useCallback(() => {
7101
- if (updateQueueRef.current) return;
7102
- updateQueueRef.current = true;
7103
- fetchData();
7095
+ console.log("[useRealtimeLineMetrics] Update queued, debouncing...");
7096
+ if (fetchTimeoutRef.current) {
7097
+ clearTimeout(fetchTimeoutRef.current);
7098
+ }
7099
+ fetchTimeoutRef.current = setTimeout(() => {
7100
+ if (updateQueueRef.current) return;
7101
+ updateQueueRef.current = true;
7102
+ console.log("[useRealtimeLineMetrics] Debounced fetch triggered");
7103
+ fetchData();
7104
+ fetchTimeoutRef.current = null;
7105
+ }, 500);
7104
7106
  }, [fetchData]);
7105
7107
  const setupSubscriptions = React21.useCallback(() => {
7106
7108
  if (channelsRef.current.length > 0) {
@@ -7178,6 +7180,10 @@ var useRealtimeLineMetrics = ({
7178
7180
  }
7179
7181
  setupSubscriptions();
7180
7182
  return () => {
7183
+ if (fetchTimeoutRef.current) {
7184
+ clearTimeout(fetchTimeoutRef.current);
7185
+ fetchTimeoutRef.current = null;
7186
+ }
7181
7187
  if (channelsRef.current.length > 0) {
7182
7188
  channelsRef.current.forEach((channel) => {
7183
7189
  if (process.env.NODE_ENV === "development") {
@@ -8124,13 +8130,10 @@ var getWorkspaceDisplayName = (workspaceId, lineId) => {
8124
8130
  }
8125
8131
  }
8126
8132
  if (displayName) {
8127
- console.log(`getWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${displayName} (from Supabase)`);
8128
8133
  return displayName;
8129
8134
  } else {
8130
8135
  if (isInitialized) {
8131
- console.log(`getWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (not found in Supabase data)`);
8132
- } else {
8133
- console.log(`getWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (Supabase not initialized yet)`);
8136
+ console.warn(`getWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (not found in Supabase data)`);
8134
8137
  }
8135
8138
  return workspaceId;
8136
8139
  }
@@ -8167,13 +8170,10 @@ var getShortWorkspaceDisplayName = (workspaceId, lineId) => {
8167
8170
  }
8168
8171
  }
8169
8172
  if (displayName) {
8170
- console.log(`getShortWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${displayName} (from Supabase)`);
8171
8173
  return displayName;
8172
8174
  } else {
8173
8175
  if (isInitialized) {
8174
- console.log(`getShortWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (not found in Supabase data)`);
8175
- } else {
8176
- console.log(`getShortWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (Supabase not initialized yet)`);
8176
+ console.warn(`getShortWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (not found in Supabase data)`);
8177
8177
  }
8178
8178
  return workspaceId;
8179
8179
  }
@@ -8479,6 +8479,8 @@ var useAllWorkspaceMetrics = (options) => {
8479
8479
  const [loading, setLoading] = React21.useState(true);
8480
8480
  const [error, setError] = React21.useState(null);
8481
8481
  const [initialized, setInitialized] = React21.useState(false);
8482
+ const fetchTimeoutRef = React21.useRef(null);
8483
+ const isFetchingRef = React21.useRef(false);
8482
8484
  const queryShiftId = React21.useMemo(() => {
8483
8485
  const currentShift = getCurrentShift(
8484
8486
  dateTimeConfig.defaultTimezone || "Asia/Kolkata",
@@ -8497,16 +8499,15 @@ var useAllWorkspaceMetrics = (options) => {
8497
8499
  }, [entityConfig.companyId]);
8498
8500
  const schema = databaseConfig.schema ?? "public";
8499
8501
  const fetchWorkspaceMetrics = React21.useCallback(async () => {
8502
+ if (isFetchingRef.current) {
8503
+ return;
8504
+ }
8505
+ isFetchingRef.current = true;
8500
8506
  if (!initialized) {
8501
8507
  setLoading(true);
8502
8508
  }
8503
8509
  setError(null);
8504
8510
  try {
8505
- console.log("Fetching all workspace metrics with params:", {
8506
- queryDate,
8507
- queryShiftId,
8508
- metricsTable
8509
- });
8510
8511
  const configuredLineIds = getConfiguredLineIds(entityConfig);
8511
8512
  let enabledWorkspaceIds = [];
8512
8513
  for (const lineId of configuredLineIds) {
@@ -8549,13 +8550,26 @@ var useAllWorkspaceMetrics = (options) => {
8549
8550
  efficiency: item.efficiency || 0,
8550
8551
  action_threshold: item.total_day_output || 0
8551
8552
  }));
8552
- setWorkspaces(transformedData);
8553
+ setWorkspaces((prevWorkspaces) => {
8554
+ if (prevWorkspaces.length !== transformedData.length) {
8555
+ return transformedData;
8556
+ }
8557
+ const hasChanges = transformedData.some((newWs) => {
8558
+ const prevWs = prevWorkspaces.find((w) => w.workspace_uuid === newWs.workspace_uuid);
8559
+ if (!prevWs) {
8560
+ return true;
8561
+ }
8562
+ return prevWs.efficiency !== newWs.efficiency || prevWs.action_count !== newWs.action_count || prevWs.action_threshold !== newWs.action_threshold || prevWs.avg_cycle_time !== newWs.avg_cycle_time || prevWs.workspace_name !== newWs.workspace_name;
8563
+ });
8564
+ return hasChanges ? transformedData : prevWorkspaces;
8565
+ });
8553
8566
  setInitialized(true);
8554
8567
  } catch (err) {
8555
- console.error("Error fetching all workspace metrics:", err);
8568
+ console.error("[useAllWorkspaceMetrics] Error:", err.message);
8556
8569
  setError({ message: err.message, code: err.code || "FETCH_ERROR" });
8557
8570
  } finally {
8558
8571
  setLoading(false);
8572
+ isFetchingRef.current = false;
8559
8573
  }
8560
8574
  }, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId]);
8561
8575
  React21.useEffect(() => {
@@ -8563,25 +8577,37 @@ var useAllWorkspaceMetrics = (options) => {
8563
8577
  fetchWorkspaceMetrics();
8564
8578
  }
8565
8579
  const setupSubscription = () => {
8566
- const filter2 = `date=eq.${queryDate} AND shift_id=eq.${queryShiftId}`;
8567
- console.log("Setting up subscription for all workspaces with filter:", filter2);
8568
8580
  const channel2 = supabase.channel(`all-workspace-metrics-${Date.now()}`).on(
8569
8581
  "postgres_changes",
8570
8582
  {
8571
8583
  event: "*",
8572
8584
  schema,
8573
- table: metricsTable,
8574
- filter: filter2
8585
+ table: metricsTable
8575
8586
  },
8576
8587
  async (payload) => {
8577
- console.log("All workspace metrics update received:", payload);
8578
- await fetchWorkspaceMetrics();
8588
+ const data = payload.new || payload.old;
8589
+ if (data?.date !== queryDate || data?.shift_id !== queryShiftId) {
8590
+ return;
8591
+ }
8592
+ if (fetchTimeoutRef.current) {
8593
+ clearTimeout(fetchTimeoutRef.current);
8594
+ }
8595
+ fetchTimeoutRef.current = setTimeout(async () => {
8596
+ if (!isFetchingRef.current) {
8597
+ await fetchWorkspaceMetrics();
8598
+ }
8599
+ fetchTimeoutRef.current = null;
8600
+ }, 300);
8579
8601
  }
8580
8602
  ).subscribe();
8581
8603
  return channel2;
8582
8604
  };
8583
8605
  const channel = setupSubscription();
8584
8606
  return () => {
8607
+ if (fetchTimeoutRef.current) {
8608
+ clearTimeout(fetchTimeoutRef.current);
8609
+ fetchTimeoutRef.current = null;
8610
+ }
8585
8611
  if (channel) {
8586
8612
  supabase.removeChannel(channel);
8587
8613
  }
@@ -21983,7 +22009,16 @@ var VideoCard = React21__namespace.default.memo(({
21983
22009
  }
21984
22010
  );
21985
22011
  }, (prevProps, nextProps) => {
21986
- return prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.workspace_name === nextProps.workspace.workspace_name && Math.abs(prevProps.workspace.efficiency - nextProps.workspace.efficiency) < 1 && prevProps.hlsUrl === nextProps.hlsUrl && prevProps.shouldPlay === nextProps.shouldPlay && prevProps.cropping?.x === nextProps.cropping?.x && prevProps.cropping?.y === nextProps.cropping?.y && prevProps.cropping?.width === nextProps.cropping?.width && prevProps.cropping?.height === nextProps.cropping?.height;
22012
+ if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
22013
+ return false;
22014
+ }
22015
+ if (prevProps.hlsUrl !== nextProps.hlsUrl || prevProps.shouldPlay !== nextProps.shouldPlay) {
22016
+ return false;
22017
+ }
22018
+ if (prevProps.cropping?.x !== nextProps.cropping?.x || prevProps.cropping?.y !== nextProps.cropping?.y || prevProps.cropping?.width !== nextProps.cropping?.width || prevProps.cropping?.height !== nextProps.cropping?.height) {
22019
+ return false;
22020
+ }
22021
+ return true;
21987
22022
  });
21988
22023
  VideoCard.displayName = "VideoCard";
21989
22024
  var DEFAULT_HLS_URL = "https://192.168.5.9:8443/cam1.m3u8";
@@ -22279,7 +22314,7 @@ var WorkspaceMetricCardsImpl = ({
22279
22314
  /* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm h-full min-h-[150px] sm:min-h-0", children: [
22280
22315
  /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
22281
22316
  /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6 sm:py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
22282
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
22317
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
22283
22318
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
22284
22319
  "Standard: ",
22285
22320
  workspace.ideal_cycle_time?.toFixed(1) || 0,
@@ -22991,7 +23026,8 @@ var BreakNotificationPopup = ({
22991
23026
  onDismiss,
22992
23027
  isVisible = true,
22993
23028
  className = "",
22994
- lineNames = {}
23029
+ lineNames = {},
23030
+ axelImagePath = "/axel-profile.png"
22995
23031
  }) => {
22996
23032
  const [isDismissed, setIsDismissed] = React21.useState(false);
22997
23033
  const [currentTime, setCurrentTime] = React21.useState(/* @__PURE__ */ new Date());
@@ -23016,6 +23052,109 @@ var BreakNotificationPopup = ({
23016
23052
  if (!isVisible || isDismissed || activeBreaks.length === 0) {
23017
23053
  return null;
23018
23054
  }
23055
+ return /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: activeBreaks.map((breakItem, index) => /* @__PURE__ */ jsxRuntime.jsx(
23056
+ motion.div,
23057
+ {
23058
+ initial: { opacity: 0, x: 100, y: -20 },
23059
+ animate: { opacity: 1, x: 0, y: 0 },
23060
+ exit: { opacity: 0, x: 100, y: -20 },
23061
+ transition: { duration: 0.3, ease: "easeOut", delay: index * 0.1 },
23062
+ className: `fixed right-4 z-50 max-w-xs w-full ${className}`,
23063
+ style: { top: `${6 + index * 12}rem` },
23064
+ 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: [
23065
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
23066
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
23067
+ "img",
23068
+ {
23069
+ src: axelImagePath,
23070
+ alt: "Axel AI",
23071
+ className: "w-10 h-10 rounded-full object-cover border-2 border-gray-200 shadow-sm",
23072
+ onError: (e) => {
23073
+ const target = e.currentTarget;
23074
+ target.style.display = "none";
23075
+ const fallback = document.createElement("div");
23076
+ fallback.className = "w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold text-base shadow-sm border-2 border-gray-200";
23077
+ fallback.textContent = "A";
23078
+ target.parentElement?.appendChild(fallback);
23079
+ }
23080
+ }
23081
+ ) }),
23082
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
23083
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
23084
+ /* @__PURE__ */ jsxRuntime.jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
23085
+ breakItem.remarks || "Break",
23086
+ (activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500 ml-1", children: [
23087
+ "\u2022 ",
23088
+ lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}`
23089
+ ] })
23090
+ ] }),
23091
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Coffee, { className: "w-4 h-4 text-amber-500" })
23092
+ ] }),
23093
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600 leading-relaxed mb-2", children: [
23094
+ "Currently active from ",
23095
+ breakItem.startTime,
23096
+ " to ",
23097
+ breakItem.endTime,
23098
+ ". ",
23099
+ formatTime3(breakItem.elapsedMinutes),
23100
+ " elapsed of ",
23101
+ formatTime3(breakItem.duration),
23102
+ " total."
23103
+ ] }),
23104
+ /* @__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(
23105
+ "div",
23106
+ {
23107
+ className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
23108
+ style: {
23109
+ width: `${Math.min(100, Math.max(0, breakItem.elapsedMinutes / breakItem.duration * 100))}%`
23110
+ }
23111
+ }
23112
+ ) }) })
23113
+ ] })
23114
+ ] }),
23115
+ /* @__PURE__ */ jsxRuntime.jsx(
23116
+ "button",
23117
+ {
23118
+ onClick: handleDismiss,
23119
+ onTouchStart: () => {
23120
+ },
23121
+ 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",
23122
+ "aria-label": "Dismiss notification",
23123
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
23124
+ }
23125
+ )
23126
+ ] }) }) })
23127
+ },
23128
+ `${breakItem.lineId}-${breakItem.startTime}-${index}`
23129
+ )) });
23130
+ };
23131
+ var AxelNotificationPopup = ({
23132
+ suggestion,
23133
+ isVisible = true,
23134
+ onDismiss,
23135
+ className = "",
23136
+ axelImagePath = "/axel-profile.png"
23137
+ }) => {
23138
+ const [isDismissed, setIsDismissed] = React21.useState(false);
23139
+ const handleDismiss = () => {
23140
+ setIsDismissed(true);
23141
+ onDismiss?.();
23142
+ };
23143
+ if (!isVisible || isDismissed || !suggestion) {
23144
+ return null;
23145
+ }
23146
+ const getTypeIcon = () => {
23147
+ switch (suggestion.type) {
23148
+ case "improvement":
23149
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { className: "w-4 h-4 text-blue-500" });
23150
+ case "alert":
23151
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-4 h-4 text-amber-500" });
23152
+ case "insight":
23153
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4 text-purple-500" });
23154
+ default:
23155
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4 text-blue-500" });
23156
+ }
23157
+ };
23019
23158
  return /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
23020
23159
  motion.div,
23021
23160
  {
@@ -23023,58 +23162,54 @@ var BreakNotificationPopup = ({
23023
23162
  animate: { opacity: 1, x: 0, y: 0 },
23024
23163
  exit: { opacity: 0, x: 100, y: -20 },
23025
23164
  transition: { duration: 0.3, ease: "easeOut" },
23026
- className: `fixed top-24 right-4 z-50 max-w-xs w-full ${className}`,
23027
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-lg overflow-hidden", children: activeBreaks.map((breakItem, index) => /* @__PURE__ */ jsxRuntime.jsx(
23165
+ className: `fixed top-24 right-4 z-40 max-w-xs w-full ${className}`,
23166
+ 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(
23028
23167
  motion.div,
23029
23168
  {
23030
- initial: { opacity: 0, y: 20 },
23169
+ initial: { opacity: 0, y: 10 },
23031
23170
  animate: { opacity: 1, y: 0 },
23032
- transition: { delay: index * 0.1 },
23033
- className: `p-3 ${index > 0 ? "border-t border-gray-100" : ""}`,
23171
+ transition: { delay: 0.1 },
23172
+ className: "p-3",
23034
23173
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
23035
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-3 flex-1", children: [
23036
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-amber-500 rounded-full animate-pulse flex-shrink-0 mt-2" }),
23174
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
23175
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
23176
+ "img",
23177
+ {
23178
+ src: axelImagePath,
23179
+ alt: "Axel AI",
23180
+ className: "w-10 h-10 rounded-full object-cover border-2 border-gray-200 shadow-sm",
23181
+ onError: (e) => {
23182
+ const target = e.currentTarget;
23183
+ target.style.display = "none";
23184
+ const fallback = document.createElement("div");
23185
+ fallback.className = "w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold text-base shadow-sm border-2 border-gray-200";
23186
+ fallback.textContent = "A";
23187
+ target.parentElement?.appendChild(fallback);
23188
+ }
23189
+ }
23190
+ ) }),
23037
23191
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
23038
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1", children: [
23039
- /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-base sm:text-sm text-gray-900", children: breakItem.remarks || "Break" }),
23040
- (activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm sm:text-xs text-gray-500 mt-0.5", children: lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}` })
23192
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
23193
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm text-gray-900", children: suggestion.title }),
23194
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: getTypeIcon() })
23041
23195
  ] }),
23042
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm sm:text-xs text-gray-600 font-medium", children: [
23043
- breakItem.startTime,
23044
- " - ",
23045
- breakItem.endTime
23046
- ] }) }),
23047
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm sm:text-xs text-gray-500", children: [
23048
- formatTime3(breakItem.elapsedMinutes),
23049
- " / ",
23050
- formatTime3(breakItem.duration)
23051
- ] }) }),
23052
- /* @__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(
23053
- "div",
23054
- {
23055
- className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
23056
- style: {
23057
- width: `${Math.min(100, Math.max(0, breakItem.elapsedMinutes / breakItem.duration * 100))}%`
23058
- }
23059
- }
23060
- ) }) })
23196
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-600 leading-relaxed", children: suggestion.message })
23061
23197
  ] })
23062
23198
  ] }),
23063
- index === 0 && /* @__PURE__ */ jsxRuntime.jsx(
23199
+ /* @__PURE__ */ jsxRuntime.jsx(
23064
23200
  "button",
23065
23201
  {
23066
23202
  onClick: handleDismiss,
23067
23203
  onTouchStart: () => {
23068
23204
  },
23069
- 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",
23205
+ 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",
23070
23206
  "aria-label": "Dismiss notification",
23071
23207
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
23072
23208
  }
23073
23209
  )
23074
23210
  ] })
23075
- },
23076
- `${breakItem.lineId}-${breakItem.startTime}-${index}`
23077
- )) })
23211
+ }
23212
+ ) })
23078
23213
  }
23079
23214
  ) });
23080
23215
  };
@@ -25905,12 +26040,6 @@ var WorkspaceHistoryCalendar = ({
25905
26040
  ] })
25906
26041
  ] });
25907
26042
  };
25908
-
25909
- // src/lib/constants/design-tokens.ts
25910
- var designTokens = {
25911
- // Typography scale with clear hierarchy
25912
- typography: {
25913
- body: "text-sm text-gray-600"}};
25914
26043
  var WEEKDAYS3 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
25915
26044
  var getOrdinal = (n) => {
25916
26045
  const suffix = ["th", "st", "nd", "rd"];
@@ -25959,8 +26088,19 @@ var WorkspaceMonthlyHistory = ({
25959
26088
  const daysInMonth = new Date(year, month + 1, 0).getDate();
25960
26089
  const dailyData = [];
25961
26090
  let maxOutput = 0;
25962
- let totalIdealOutput = 0;
25963
- let validDaysCount = 0;
26091
+ let lastSetTarget = 0;
26092
+ for (let day = daysInMonth; day >= 1; day--) {
26093
+ const dayData = data.find((d) => {
26094
+ const date = new Date(d.date);
26095
+ return date.getDate() === day;
26096
+ });
26097
+ const shiftData = dayData ? selectedShift === "day" ? dayData.dayShift : dayData.nightShift : null;
26098
+ const idealOutput = shiftData ? shiftData.idealOutput : 0;
26099
+ if (idealOutput > 0) {
26100
+ lastSetTarget = idealOutput;
26101
+ break;
26102
+ }
26103
+ }
25964
26104
  for (let day = 1; day <= daysInMonth; day++) {
25965
26105
  const dayData = data.find((d) => {
25966
26106
  const date = new Date(d.date);
@@ -25970,11 +26110,7 @@ var WorkspaceMonthlyHistory = ({
25970
26110
  const output = shiftData && hasRealData(shiftData) ? shiftData.output : 0;
25971
26111
  const idealOutput = shiftData ? shiftData.idealOutput : 0;
25972
26112
  if (output > maxOutput) maxOutput = output;
25973
- if (idealOutput > 0) {
25974
- totalIdealOutput += idealOutput;
25975
- validDaysCount++;
25976
- }
25977
- const color2 = output >= idealOutput ? "#00AB45" : "#E34329";
26113
+ const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
25978
26114
  dailyData.push({
25979
26115
  hour: getOrdinal(day),
25980
26116
  // Using ordinal format (1st, 2nd, 3rd, etc.)
@@ -25989,14 +26125,13 @@ var WorkspaceMonthlyHistory = ({
25989
26125
  // Not used but keeps structure consistent
25990
26126
  });
25991
26127
  }
25992
- const avgIdealOutput = validDaysCount > 0 ? totalIdealOutput / validDaysCount : 0;
25993
- const calculatedMax = Math.max(maxOutput, avgIdealOutput);
26128
+ const calculatedMax = Math.max(maxOutput, lastSetTarget);
25994
26129
  const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
25995
- return { data: dailyData, maxOutput, avgIdealOutput, yAxisMax };
26130
+ return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
25996
26131
  }, [data, month, year, selectedShift]);
25997
26132
  const yAxisTicks = React21.useMemo(() => {
25998
26133
  const max = chartData.yAxisMax;
25999
- const target = chartData.avgIdealOutput;
26134
+ const target = chartData.lastSetTarget;
26000
26135
  if (!max || max <= 0) return void 0;
26001
26136
  const desiredIntervals = 4;
26002
26137
  const roughStep = max / desiredIntervals;
@@ -26014,7 +26149,7 @@ var WorkspaceMonthlyHistory = ({
26014
26149
  ticks.push(Math.round(target));
26015
26150
  }
26016
26151
  return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
26017
- }, [chartData.yAxisMax, chartData.avgIdealOutput]);
26152
+ }, [chartData.yAxisMax, chartData.lastSetTarget]);
26018
26153
  const pieChartData = React21.useMemo(() => {
26019
26154
  const validShifts = data.map((d) => selectedShift === "day" ? d.dayShift : d.nightShift).filter(hasRealData);
26020
26155
  if (validShifts.length === 0) return [];
@@ -26088,10 +26223,13 @@ var WorkspaceMonthlyHistory = ({
26088
26223
  }
26089
26224
  }, [workspaceId, onShiftChange]);
26090
26225
  if (monthlyDataLoading) {
26091
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center space-y-4", children: [
26092
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-12 w-12 animate-spin rounded-full border-4 border-blue-200", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 rounded-full border-4 border-blue-600 border-t-transparent" }) }) }),
26093
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: designTokens.typography.body + " font-medium", children: "Loading monthly performance data..." })
26094
- ] }) });
26226
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(
26227
+ OptifyeLogoLoader_default,
26228
+ {
26229
+ size: "lg",
26230
+ message: "Loading monthly performance data..."
26231
+ }
26232
+ ) });
26095
26233
  }
26096
26234
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col gap-2 min-h-0 overflow-y-auto pb-6 ${className}`, children: [
26097
26235
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 border border-gray-200 rounded-lg p-1 bg-gray-50", children: [
@@ -26326,7 +26464,7 @@ var WorkspaceMonthlyHistory = ({
26326
26464
  tick: (props) => {
26327
26465
  const { x, y, payload } = props;
26328
26466
  const value = Math.round(payload.value);
26329
- const targetValue = Math.round(chartData.avgIdealOutput);
26467
+ const targetValue = Math.round(chartData.lastSetTarget);
26330
26468
  const isTarget = Math.abs(value - targetValue) < 1 && targetValue > 0;
26331
26469
  return /* @__PURE__ */ jsxRuntime.jsx(
26332
26470
  "text",
@@ -26350,10 +26488,10 @@ var WorkspaceMonthlyHistory = ({
26350
26488
  content: CustomTooltip
26351
26489
  }
26352
26490
  ),
26353
- chartData.avgIdealOutput > 0 && /* @__PURE__ */ jsxRuntime.jsx(
26491
+ chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
26354
26492
  recharts.ReferenceLine,
26355
26493
  {
26356
- y: chartData.avgIdealOutput,
26494
+ y: chartData.lastSetTarget,
26357
26495
  stroke: "#E34329",
26358
26496
  strokeDasharray: "5 5",
26359
26497
  strokeWidth: 2
@@ -26411,11 +26549,11 @@ var WorkspaceMonthlyHistory = ({
26411
26549
  ]
26412
26550
  }
26413
26551
  ) }) }),
26414
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center gap-6 mt-3", children: chartData.avgIdealOutput > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
26552
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center gap-6 mt-3", children: chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
26415
26553
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-0.5 border-t-2 border-dashed", style: { borderColor: "#E34329" } }),
26416
26554
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-600", children: [
26417
26555
  "Target: ",
26418
- Math.round(chartData.avgIdealOutput),
26556
+ Math.round(chartData.lastSetTarget),
26419
26557
  " units/day"
26420
26558
  ] })
26421
26559
  ] }) })
@@ -26528,7 +26666,7 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
26528
26666
  doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
26529
26667
  };
26530
26668
  doc.setFillColor(245, 245, 245);
26531
- doc.roundedRect(15, 95, 180, 70, 3, 3, "F");
26669
+ doc.roundedRect(15, 95, 180, 60, 3, 3, "F");
26532
26670
  doc.setFontSize(18);
26533
26671
  doc.setFont("helvetica", "bold");
26534
26672
  doc.setTextColor(40, 40, 40);
@@ -26557,34 +26695,29 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
26557
26695
  doc.text("PPH (Pieces Per Hour):", 25, kpiStartY + kpiSpacing * 3);
26558
26696
  doc.setFont("helvetica", "bold");
26559
26697
  doc.text(`${workspace.avg_pph.toFixed(1)} (Standard: ${workspace.pph_threshold.toFixed(1)})`, 120, kpiStartY + kpiSpacing * 3);
26560
- createKPIBox(kpiStartY + kpiSpacing * 4);
26561
- doc.setFont("helvetica", "normal");
26562
- doc.text("Rank:", 25, kpiStartY + kpiSpacing * 4);
26563
- doc.setFont("helvetica", "bold");
26564
- doc.text(`${workspace.workspace_rank} of ${workspace.total_workspaces}`, 120, kpiStartY + kpiSpacing * 4);
26565
26698
  doc.setDrawColor(180, 180, 180);
26566
26699
  doc.setLineWidth(0.8);
26567
- doc.line(20, 180, 190, 180);
26700
+ doc.line(20, 170, 190, 170);
26568
26701
  doc.setFillColor(245, 245, 245);
26569
- doc.roundedRect(15, 185, 180, 85, 3, 3, "F");
26702
+ doc.roundedRect(15, 175, 180, 85, 3, 3, "F");
26570
26703
  doc.setFontSize(18);
26571
26704
  doc.setFont("helvetica", "bold");
26572
26705
  doc.setTextColor(40, 40, 40);
26573
- doc.text("Hourly Performance", 20, 195);
26706
+ doc.text("Hourly Performance", 20, 185);
26574
26707
  doc.setTextColor(0, 0, 0);
26575
26708
  doc.setFontSize(11);
26576
26709
  doc.setFont("helvetica", "bold");
26577
26710
  doc.setFillColor(245, 245, 245);
26578
- doc.roundedRect(20, 187, 170, 8, 1, 1, "F");
26579
- doc.text("Time Range", 25, 192);
26580
- doc.text("Output", 95, 192);
26581
- doc.text("Target", 135, 192);
26582
- doc.text("Status", 170, 192);
26711
+ doc.roundedRect(20, 177, 170, 8, 1, 1, "F");
26712
+ doc.text("Time Range", 25, 182);
26713
+ doc.text("Output", 95, 182);
26714
+ doc.text("Target", 135, 182);
26715
+ doc.text("Status", 170, 182);
26583
26716
  doc.setLineWidth(0.2);
26584
26717
  doc.setDrawColor(220, 220, 220);
26585
- doc.line(20, 195, 190, 195);
26718
+ doc.line(20, 185, 190, 185);
26586
26719
  doc.setFont("helvetica", "normal");
26587
- let yPos = 201;
26720
+ let yPos = 191;
26588
26721
  const hourlyData = workspace.hourly_action_counts || [];
26589
26722
  const hourlyTarget = workspace.pph_threshold;
26590
26723
  hourlyData.forEach((output, index) => {
@@ -27261,6 +27394,36 @@ var TimePickerDropdown = ({
27261
27394
  ] })
27262
27395
  ] });
27263
27396
  };
27397
+ var ERROR_MAPPING = {
27398
+ 1: {
27399
+ // MEDIA_ERR_ABORTED
27400
+ code: 1,
27401
+ type: "recoverable" /* RECOVERABLE */,
27402
+ message: "Video loading was interrupted",
27403
+ canRetry: true
27404
+ },
27405
+ 2: {
27406
+ // MEDIA_ERR_NETWORK
27407
+ code: 2,
27408
+ type: "recoverable" /* RECOVERABLE */,
27409
+ message: "Network error - please check your internet connection",
27410
+ canRetry: true
27411
+ },
27412
+ 3: {
27413
+ // MEDIA_ERR_DECODE
27414
+ code: 3,
27415
+ type: "non_recoverable" /* NON_RECOVERABLE */,
27416
+ message: "Stream corrupted due to internet connection",
27417
+ canRetry: false
27418
+ },
27419
+ 4: {
27420
+ // MEDIA_ERR_SRC_NOT_SUPPORTED
27421
+ code: 4,
27422
+ type: "non_recoverable" /* NON_RECOVERABLE */,
27423
+ message: "Video format not supported by your browser. Please use Google Chrome.",
27424
+ canRetry: false
27425
+ }
27426
+ };
27264
27427
  var videoPlayerStyles = `
27265
27428
  .video-player-container {
27266
27429
  width: 100%;
@@ -27491,8 +27654,21 @@ var VideoPlayer = React21__namespace.default.forwardRef(({
27491
27654
  player.on("seeked", () => onSeeked?.(player));
27492
27655
  player.on("error", () => {
27493
27656
  const error = player.error();
27494
- console.error("Video.js error:", error);
27495
- onError?.(player, error);
27657
+ const errorCode = error?.code ?? 0;
27658
+ const errorInfo = ERROR_MAPPING[errorCode] || {
27659
+ code: errorCode || 0,
27660
+ type: "non_recoverable" /* NON_RECOVERABLE */,
27661
+ message: "Unknown playback error occurred",
27662
+ canRetry: false
27663
+ };
27664
+ console.error("[VideoPlayer] Video.js error:", {
27665
+ code: errorCode,
27666
+ type: errorInfo.type,
27667
+ message: errorInfo.message,
27668
+ canRetry: errorInfo.canRetry,
27669
+ originalError: error
27670
+ });
27671
+ onError?.(player, errorInfo);
27496
27672
  });
27497
27673
  if (src) {
27498
27674
  const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
@@ -28335,7 +28511,10 @@ var FilterDialogTrigger = ({
28335
28511
  }
28336
28512
  );
28337
28513
  };
28338
- var getSeverityIcon = (severity) => {
28514
+ var getSeverityIcon = (severity, categoryId) => {
28515
+ if (categoryId === "idle_time" || categoryId === "low_value" || categoryId === "longest-idles") {
28516
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-red-500" });
28517
+ }
28339
28518
  switch (severity) {
28340
28519
  case "high":
28341
28520
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-red-500" });
@@ -28562,17 +28741,17 @@ var FileManagerFilters = ({
28562
28741
  const colorClasses = getColorClasses(category.color);
28563
28742
  const clipNodes = filteredClips.map((clip, index) => {
28564
28743
  const timeString = new Date(clip.clip_timestamp).toLocaleTimeString("en-US", {
28565
- hour12: false,
28566
- hour: "2-digit",
28744
+ hour12: true,
28745
+ hour: "numeric",
28567
28746
  minute: "2-digit",
28568
28747
  timeZone: timezone
28569
28748
  // Use database timezone for display
28570
28749
  });
28571
28750
  return {
28572
28751
  id: clip.id,
28573
- label: `${timeString} - ${clip.description}${clip.duration && category.id !== "idle_time" ? ` (${clip.duration.toFixed(1)}s)` : ""}`,
28752
+ label: `${timeString}${clip.duration && category.id !== "idle_time" ? ` - (${clip.duration.toFixed(1)}s)` : ""}`,
28574
28753
  type: "video",
28575
- icon: getSeverityIcon(clip.severity),
28754
+ icon: getSeverityIcon(clip.severity, category.id),
28576
28755
  timestamp: clip.clip_timestamp,
28577
28756
  severity: clip.severity,
28578
28757
  clipId: clip.clipId,
@@ -28612,9 +28791,9 @@ var FileManagerFilters = ({
28612
28791
  children: (percentileClips["fast-cycles"] || []).map((clip, index) => ({
28613
28792
  id: clip.id,
28614
28793
  // Remove prefix to match currentVideoId
28615
- label: `${clip.timestamp} - ${clip.description}${clip.cycle_time_seconds ? ` (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28794
+ label: `${clip.timestamp}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28616
28795
  type: "video",
28617
- icon: getSeverityIcon(clip.severity),
28796
+ icon: getSeverityIcon(clip.severity, "fast-cycles"),
28618
28797
  timestamp: clip.creation_timestamp,
28619
28798
  severity: clip.severity,
28620
28799
  clipId: clip.id,
@@ -28635,9 +28814,9 @@ var FileManagerFilters = ({
28635
28814
  children: (percentileClips["slow-cycles"] || []).map((clip, index) => ({
28636
28815
  id: clip.id,
28637
28816
  // Remove prefix to match currentVideoId
28638
- label: `${clip.timestamp} - ${clip.description}${clip.cycle_time_seconds ? ` (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28817
+ label: `${clip.timestamp}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
28639
28818
  type: "video",
28640
- icon: getSeverityIcon(clip.severity),
28819
+ icon: getSeverityIcon(clip.severity, "slow-cycles"),
28641
28820
  timestamp: clip.creation_timestamp,
28642
28821
  severity: clip.severity,
28643
28822
  clipId: clip.id,
@@ -28658,9 +28837,9 @@ var FileManagerFilters = ({
28658
28837
  isPercentile: true,
28659
28838
  children: (percentileClips['idle-times'] || []).map((clip, index) => ({
28660
28839
  id: clip.id, // Remove prefix to match currentVideoId
28661
- label: `${clip.timestamp} - ${clip.description}${clip.cycle_time_seconds ? ` (${clip.cycle_time_seconds.toFixed(1)}s)` : ''}`,
28840
+ label: `${clip.timestamp}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ''}`,
28662
28841
  type: 'video' as const,
28663
- icon: getSeverityIcon(clip.severity),
28842
+ icon: getSeverityIcon(clip.severity, 'longest-idles'),
28664
28843
  timestamp: clip.creation_timestamp,
28665
28844
  severity: clip.severity,
28666
28845
  clipId: clip.id,
@@ -28670,7 +28849,7 @@ var FileManagerFilters = ({
28670
28849
  ];
28671
28850
  percentileCategories.forEach((category) => {
28672
28851
  if (category.count > 0 && shouldShowCategory(category.id)) {
28673
- tree.unshift(category);
28852
+ tree.push(category);
28674
28853
  }
28675
28854
  });
28676
28855
  return tree;
@@ -28715,7 +28894,7 @@ var FileManagerFilters = ({
28715
28894
  /* @__PURE__ */ jsxRuntime.jsxs(
28716
28895
  "div",
28717
28896
  {
28718
- className: `flex items-center cursor-pointer transition-all duration-300 ease-out group relative overflow-hidden ${node.type === "category" && depth === 0 ? `py-3 px-4 rounded-2xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-blue-50/30 hover:shadow-lg hover:shadow-blue-100/20 hover:scale-[1.02] hover:-translate-y-0.5 ${isActive ? "bg-gradient-to-r from-blue-50 via-blue-50/80 to-indigo-50/60 border border-blue-200/50 shadow-lg shadow-blue-100/30 scale-[1.02]" : "border border-transparent"}` : `py-2 px-3 rounded-xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-slate-50/50 hover:shadow-sm ${isActive ? "bg-gradient-to-r from-blue-50/80 to-blue-50/40 border border-blue-200/30 shadow-sm" : "border border-transparent"} ${isCurrentVideo ? "bg-gradient-to-r from-emerald-50 to-green-50/60 border border-emerald-200/50 shadow-md shadow-emerald-100/20" : ""}`} ${node.type === "video" ? "ml-6" : ""}`,
28897
+ className: `flex items-center cursor-pointer transition-all duration-300 ease-out group relative overflow-hidden ${node.type === "category" && depth === 0 ? `py-3 px-4 rounded-2xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-blue-50/30 hover:shadow-lg hover:shadow-blue-100/20 hover:scale-[1.02] hover:-translate-y-0.5 ${isActive ? "bg-gradient-to-r from-blue-50 via-blue-50/80 to-indigo-50/60 border border-blue-200/50 shadow-lg shadow-blue-100/30 scale-[1.02]" : "border border-transparent"}` : `py-2 px-3 rounded-xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-slate-50/50 hover:shadow-sm ${isActive ? "bg-gradient-to-r from-blue-50/80 to-blue-50/40 border border-blue-200/30 shadow-sm" : "border border-transparent"} ${isCurrentVideo ? "bg-gradient-to-r from-blue-50 to-blue-50/60 border border-blue-200/50 shadow-md shadow-blue-100/20" : ""}`} ${node.type === "video" ? "ml-6" : ""}`,
28719
28898
  onClick: () => handleNodeClick(node),
28720
28899
  children: [
28721
28900
  hasChildren && /* @__PURE__ */ jsxRuntime.jsx(
@@ -28732,13 +28911,10 @@ var FileManagerFilters = ({
28732
28911
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 mr-3 ${node.type === "category" || node.type === "percentile-category" ? "p-2 rounded-lg shadow-sm group-hover:scale-110 transition-transform duration-200" : "p-0.5"} ${colorClasses && (node.type === "category" || node.type === "percentile-category") ? `${colorClasses.bg} border border-white/60` : ""}`, children: node.icon }),
28733
28912
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between", children: [
28734
28913
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
28735
- /* @__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-emerald-700 font-bold" : ""} group-hover:text-slate-900 transition-colors duration-200`, children: node.label }),
28914
+ /* @__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 }),
28736
28915
  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 }),
28737
28916
  node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
28738
- node.type === "video" && node.severity && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${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: [
28739
- node.severity,
28740
- " priority"
28741
- ] }) })
28917
+ 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" }) })
28742
28918
  ] }),
28743
28919
  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 }) })
28744
28920
  ] })
@@ -29387,7 +29563,12 @@ var BottlenecksContent = ({
29387
29563
  } catch (err) {
29388
29564
  console.error("[BottlenecksContent] Error fetching clip counts:", err);
29389
29565
  if (isMountedRef.current) {
29390
- setError("Failed to load clip counts. Please try again.");
29566
+ setError({
29567
+ type: "fatal",
29568
+ message: "Failed to load clip counts. Please try again.",
29569
+ canSkip: false,
29570
+ canRetry: true
29571
+ });
29391
29572
  setIsLoading(false);
29392
29573
  }
29393
29574
  } finally {
@@ -29471,7 +29652,12 @@ var BottlenecksContent = ({
29471
29652
  } catch (err) {
29472
29653
  console.error("Error loading first video for category:", err);
29473
29654
  if (isMountedRef.current) {
29474
- setError("Failed to load clips. Please try again.");
29655
+ setError({
29656
+ type: "fatal",
29657
+ message: "Failed to load clips. Please try again.",
29658
+ canSkip: false,
29659
+ canRetry: true
29660
+ });
29475
29661
  setIsCategoryLoading(false);
29476
29662
  }
29477
29663
  } finally {
@@ -29753,7 +29939,12 @@ var BottlenecksContent = ({
29753
29939
  } catch (error2) {
29754
29940
  console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
29755
29941
  if (isMountedRef.current) {
29756
- setError("Failed to load selected clip. Please try again.");
29942
+ setError({
29943
+ type: "fatal",
29944
+ message: "Failed to load selected clip. Please try again.",
29945
+ canSkip: true,
29946
+ canRetry: true
29947
+ });
29757
29948
  clearLoadingState();
29758
29949
  }
29759
29950
  }
@@ -29776,7 +29967,12 @@ var BottlenecksContent = ({
29776
29967
  }
29777
29968
  } catch (error2) {
29778
29969
  console.error(`[BottlenecksContent] Error in legacy loadAndPlayClip:`, error2);
29779
- setError("Failed to load selected clip. Please try again.");
29970
+ setError({
29971
+ type: "fatal",
29972
+ message: "Failed to load selected clip. Please try again.",
29973
+ canSkip: true,
29974
+ canRetry: true
29975
+ });
29780
29976
  setIsNavigating(false);
29781
29977
  }
29782
29978
  }, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById]);
@@ -29816,7 +30012,12 @@ var BottlenecksContent = ({
29816
30012
  }
29817
30013
  } catch (error2) {
29818
30014
  console.error(`[handleNext] Error navigating:`, error2);
29819
- setError("Failed to navigate to next clip");
30015
+ setError({
30016
+ type: "fatal",
30017
+ message: "Failed to navigate to next clip",
30018
+ canSkip: true,
30019
+ canRetry: true
30020
+ });
29820
30021
  clearLoadingState();
29821
30022
  }
29822
30023
  }, [clearLoadingState, s3ClipsService]);
@@ -29852,7 +30053,12 @@ var BottlenecksContent = ({
29852
30053
  }
29853
30054
  } catch (error2) {
29854
30055
  console.error(`[handlePrevious] Error navigating:`, error2);
29855
- setError("Failed to navigate to previous clip");
30056
+ setError({
30057
+ type: "fatal",
30058
+ message: "Failed to navigate to previous clip",
30059
+ canSkip: true,
30060
+ canRetry: true
30061
+ });
29856
30062
  clearLoadingState();
29857
30063
  }
29858
30064
  }, [clearLoadingState, s3ClipsService]);
@@ -29865,7 +30071,7 @@ var BottlenecksContent = ({
29865
30071
  const handleVideoReady = React21.useCallback((player) => {
29866
30072
  console.log("Video.js player ready - NOT clearing loading (wait for playing event)");
29867
30073
  videoRetryCountRef.current = 0;
29868
- if (error?.includes("Retrying")) {
30074
+ if (error?.isRetrying) {
29869
30075
  setError(null);
29870
30076
  }
29871
30077
  }, [error]);
@@ -29910,20 +30116,53 @@ var BottlenecksContent = ({
29910
30116
  }, [clearLoadingState]);
29911
30117
  const handleVideoLoadingChange = React21.useCallback((isLoading2) => {
29912
30118
  console.log(`[BottlenecksContent] Video loading state changed: ${isLoading2}`);
30119
+ if (error && error.type === "fatal") {
30120
+ console.log(`[BottlenecksContent] Ignoring loading state change - fatal error is showing`);
30121
+ return;
30122
+ }
29913
30123
  setIsVideoBuffering(isLoading2);
29914
- }, []);
30124
+ }, [error]);
29915
30125
  const handleVideoEnded = React21.useCallback((player) => {
29916
30126
  handleNext();
29917
30127
  }, [handleNext]);
29918
30128
  const videoRetryCountRef = React21.useRef(0);
29919
- const handleVideoError = React21.useCallback((player, error2) => {
29920
- console.error("Video.js error:", error2);
30129
+ const handleVideoError = React21.useCallback((player, errorInfo) => {
30130
+ console.error("[BottlenecksContent] Video.js error:", errorInfo);
29921
30131
  setIsPlaying(false);
30132
+ setIsVideoBuffering(false);
30133
+ const errorCode = errorInfo?.code || 0;
30134
+ const canRetry = errorInfo?.canRetry ?? false;
30135
+ const errorMessage = errorInfo?.message || "Unknown error";
30136
+ console.log(`[Video Error] Code: ${errorCode}, Can Retry: ${canRetry}, Message: ${errorMessage}`);
30137
+ if (!canRetry) {
30138
+ console.log("[Video Error] Non-recoverable error - showing error overlay immediately");
30139
+ setError({
30140
+ type: "fatal",
30141
+ code: errorCode,
30142
+ message: errorMessage,
30143
+ canSkip: true,
30144
+ canRetry: false
30145
+ });
30146
+ clearLoadingState();
30147
+ videoRetryCountRef.current = 0;
30148
+ trackCoreEvent("clips_video_error_non_recoverable", {
30149
+ workspaceId,
30150
+ category: activeFilterRef.current,
30151
+ videoId: currentVideo?.id,
30152
+ errorCode,
30153
+ errorMessage
30154
+ });
30155
+ return;
30156
+ }
29922
30157
  if (videoRetryCountRef.current < 3 && currentVideo) {
29923
30158
  videoRetryCountRef.current++;
29924
30159
  const retryDelay = 1e3 * videoRetryCountRef.current;
29925
- console.log(`[Video Error] Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
29926
- setError(`Retrying... (${videoRetryCountRef.current}/3)`);
30160
+ console.log(`[Video Error] Recoverable error - Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
30161
+ setError({
30162
+ type: "retrying",
30163
+ message: `Retrying... (${videoRetryCountRef.current}/3)`,
30164
+ isRetrying: true
30165
+ });
29927
30166
  setTimeout(() => {
29928
30167
  if (videoRef.current && currentVideo && isMountedRef.current) {
29929
30168
  setError(null);
@@ -29931,16 +30170,26 @@ var BottlenecksContent = ({
29931
30170
  }
29932
30171
  }, retryDelay);
29933
30172
  } else {
29934
- setError("Video failed to load after 3 attempts. The stream may be corrupted.");
30173
+ console.log("[Video Error] Retries exhausted - showing final error overlay");
30174
+ setError({
30175
+ type: "fatal",
30176
+ code: errorCode,
30177
+ message: errorMessage,
30178
+ canSkip: true,
30179
+ canRetry: true
30180
+ // Allow manual retry for network errors
30181
+ });
29935
30182
  videoRetryCountRef.current = 0;
29936
30183
  trackCoreEvent("clips_video_error_final", {
29937
30184
  workspaceId,
29938
30185
  category: activeFilterRef.current,
29939
30186
  videoId: currentVideo?.id,
29940
- attempts: videoRetryCountRef.current
30187
+ errorCode,
30188
+ errorMessage,
30189
+ attempts: 3
29941
30190
  });
29942
30191
  }
29943
- }, [currentVideo, workspaceId]);
30192
+ }, [currentVideo, workspaceId, clearLoadingState]);
29944
30193
  React21.useEffect(() => {
29945
30194
  isMountedRef.current = true;
29946
30195
  return () => {
@@ -29958,9 +30207,13 @@ var BottlenecksContent = ({
29958
30207
  }, [s3ClipsService]);
29959
30208
  React21.useEffect(() => {
29960
30209
  if (filteredVideos.length > 0 && currentIndex < filteredVideos.length) {
30210
+ if (error && error.type === "fatal") {
30211
+ console.log("[BottlenecksContent] Not clearing fatal error on video change - let user handle it");
30212
+ return;
30213
+ }
29961
30214
  setError(null);
29962
30215
  }
29963
- }, [currentIndex, filteredVideos]);
30216
+ }, [currentIndex, filteredVideos, error]);
29964
30217
  React21.useEffect(() => {
29965
30218
  if (!isTransitioning && pendingVideo) {
29966
30219
  const timer = setTimeout(() => {
@@ -30035,11 +30288,11 @@ var BottlenecksContent = ({
30035
30288
  if ((isLoading || clipTypesLoading) && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
30036
30289
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow p-4 flex items-center justify-center h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
30037
30290
  }
30038
- if (error || clipTypesError) {
30291
+ if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
30039
30292
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
30040
30293
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
30041
30294
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "Error Loading Clips" }),
30042
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error || clipTypesError })
30295
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
30043
30296
  ] });
30044
30297
  }
30045
30298
  const categoriesToShow = clipTypes.length > 0 ? clipTypes : [];
@@ -30086,7 +30339,7 @@ var BottlenecksContent = ({
30086
30339
  ),
30087
30340
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
30088
30341
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children: categoryMetadata.length > 0 ? `${currentMetadataIndex + 1} / ${categoryMetadata.length}` : "0 / 0" }),
30089
- error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-red-600 font-medium", children: error })
30342
+ error && error.type === "retrying" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-orange-600 font-medium", children: error.message })
30090
30343
  ] }),
30091
30344
  /* @__PURE__ */ jsxRuntime.jsx(
30092
30345
  "button",
@@ -30100,7 +30353,6 @@ var BottlenecksContent = ({
30100
30353
  )
30101
30354
  ] })
30102
30355
  ] }) }),
30103
- /* Show video if we have filtered videos and current video, or show loading */
30104
30356
  filteredVideos.length > 0 && currentVideo ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full group", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
30105
30357
  /* @__PURE__ */ jsxRuntime.jsx(
30106
30358
  CroppedVideoPlayer,
@@ -30132,30 +30384,44 @@ var BottlenecksContent = ({
30132
30384
  }
30133
30385
  }
30134
30386
  ),
30135
- (isTransitioning || isVideoBuffering && isInitialLoading) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
30136
- !isTransitioning && isVideoBuffering && !isInitialLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
30137
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/80 text-white p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-md", children: [
30387
+ (isTransitioning || isVideoBuffering && isInitialLoading) && !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
30388
+ !isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
30389
+ error && error.type === "retrying" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
30390
+ /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md" }),
30391
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
30392
+ ] }) }),
30393
+ error && error.type === "fatal" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/90 text-white p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-md", children: [
30138
30394
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-16 h-16 mx-auto mb-4 text-red-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 16.5c-.77.833.192 2.5 1.732 2.5z" }) }),
30139
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: "Video Stream Error" }),
30140
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-300 mb-4", children: "The video stream appears to be corrupted or unavailable. This may be due to:" }),
30141
- /* @__PURE__ */ jsxRuntime.jsxs("ul", { className: "text-sm text-gray-300 text-left space-y-1 mb-4", children: [
30142
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: "\u2022 Incomplete video encoding" }),
30143
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: "\u2022 Network connectivity issues" }),
30144
- /* @__PURE__ */ jsxRuntime.jsx("li", { children: "\u2022 Server processing errors" })
30145
- ] }),
30146
- /* @__PURE__ */ jsxRuntime.jsx(
30147
- "button",
30148
- {
30149
- onClick: () => {
30150
- setError(null);
30151
- if (videoRef.current) {
30152
- videoRef.current.dispose();
30153
- }
30154
- },
30155
- className: "px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded text-sm font-medium",
30156
- children: "Retry"
30157
- }
30158
- )
30395
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: error.code === 3 ? "Stream Corrupted" : error.code === 4 ? "Format Not Supported" : error.code === 2 ? "Network Error" : error.code === 1 ? "Loading Interrupted" : "Playback Error" }),
30396
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
30397
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 justify-center", children: [
30398
+ error.canSkip && /* @__PURE__ */ jsxRuntime.jsx(
30399
+ "button",
30400
+ {
30401
+ onClick: () => {
30402
+ setError(null);
30403
+ videoRetryCountRef.current = 0;
30404
+ handleNext();
30405
+ },
30406
+ className: "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 rounded-md text-sm font-medium transition-colors",
30407
+ children: "Skip to Next Clip"
30408
+ }
30409
+ ),
30410
+ error.canRetry && /* @__PURE__ */ jsxRuntime.jsx(
30411
+ "button",
30412
+ {
30413
+ onClick: () => {
30414
+ setError(null);
30415
+ videoRetryCountRef.current = 0;
30416
+ if (videoRef.current) {
30417
+ videoRef.current.dispose();
30418
+ }
30419
+ },
30420
+ className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
30421
+ children: "Retry"
30422
+ }
30423
+ )
30424
+ ] })
30159
30425
  ] }) }),
30160
30426
  (currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || currentVideo.type === "idle_time" || currentVideo.type === "low_value" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 left-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
30161
30427
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" || currentVideo.type === "idle_time" ? "bg-purple-400" : isPercentileCategory(activeFilterRef.current) ? activeFilterRef.current === "fast-cycles" ? "bg-green-600" : activeFilterRef.current === "slow-cycles" ? "bg-red-700" : "bg-orange-500" : currentVideo.type === "cycle_completion" ? "bg-blue-600" : "bg-gray-500"} mr-2 animate-pulse` }),
@@ -36889,6 +37155,8 @@ function HomeView({
36889
37155
  const [errorMessage, setErrorMessage] = React21.useState(null);
36890
37156
  const [displayNamesInitialized, setDisplayNamesInitialized] = React21.useState(false);
36891
37157
  const [hasInitialDataLoaded, setHasInitialDataLoaded] = React21.useState(false);
37158
+ const [axelSuggestion, setAxelSuggestion] = React21.useState(null);
37159
+ const [showAxelNotification, setShowAxelNotification] = React21.useState(false);
36892
37160
  const dashboardConfig = useDashboardConfig();
36893
37161
  const timezone = useAppTimezone();
36894
37162
  React21.useEffect(() => {
@@ -36963,12 +37231,7 @@ function HomeView({
36963
37231
  }, []);
36964
37232
  const handleWorkspaceHoverEnd = React21.useCallback((workspaceId) => {
36965
37233
  }, []);
36966
- const memoizedWorkspaceMetrics = React21.useMemo(() => workspaceMetrics, [
36967
- // Only update reference if meaningful properties change
36968
- workspaceMetrics.length,
36969
- // Use stable string representation instead of spreading array
36970
- JSON.stringify(workspaceMetrics.map((w) => `${w.workspace_uuid}-${Math.round(w.efficiency)}-${w.trend}`))
36971
- ]);
37234
+ const memoizedWorkspaceMetrics = workspaceMetrics;
36972
37235
  const memoizedKPIs = React21.useMemo(() => kpis, [
36973
37236
  // Only update reference when values change by at least 1%
36974
37237
  kpis?.efficiency?.value ? Math.round(kpis.efficiency.value) : null,
@@ -36996,6 +37259,9 @@ function HomeView({
36996
37259
  setIsChangingFilter(true);
36997
37260
  setSelectedLineId(value);
36998
37261
  }, []);
37262
+ const handleDismissAxelNotification = React21.useCallback(() => {
37263
+ setShowAxelNotification(false);
37264
+ }, []);
36999
37265
  React21.useEffect(() => {
37000
37266
  if (!metricsLoading && !kpisLoading && isChangingFilter) {
37001
37267
  if (workspaceMetrics.length > 0 || selectedLineId === factoryViewId) {
@@ -37107,6 +37373,14 @@ function HomeView({
37107
37373
  lineNames,
37108
37374
  isVisible: !breaksLoading && !breaksError
37109
37375
  }
37376
+ ),
37377
+ /* @__PURE__ */ jsxRuntime.jsx(
37378
+ AxelNotificationPopup,
37379
+ {
37380
+ suggestion: axelSuggestion,
37381
+ isVisible: showAxelNotification,
37382
+ onDismiss: handleDismissAxelNotification
37383
+ }
37110
37384
  )
37111
37385
  ] })
37112
37386
  }
@@ -38540,7 +38814,7 @@ var KPIsOverviewView = ({
38540
38814
  var KPIsOverviewView_default = KPIsOverviewView;
38541
38815
  var IsolatedTimer = React21.memo(() => {
38542
38816
  return /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {});
38543
- }, () => true);
38817
+ });
38544
38818
  IsolatedTimer.displayName = "IsolatedTimer";
38545
38819
  var HeaderRibbon = React21.memo(({
38546
38820
  currentDate,
@@ -38548,42 +38822,49 @@ var HeaderRibbon = React21.memo(({
38548
38822
  shiftId,
38549
38823
  getShiftIcon,
38550
38824
  getShiftName
38551
- }) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
38552
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
38553
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-gray-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: currentMobileDate }) }),
38554
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
38555
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: getShiftIcon(shiftId) }),
38556
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: getShiftName(shiftId) })
38825
+ }) => {
38826
+ const shiftIcon = React21.useMemo(() => getShiftIcon(shiftId), [getShiftIcon, shiftId]);
38827
+ const shiftName = React21.useMemo(() => getShiftName(shiftId), [getShiftName, shiftId]);
38828
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
38829
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
38830
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-gray-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: currentMobileDate }) }),
38831
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
38832
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: shiftIcon }),
38833
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: shiftName })
38834
+ ] }),
38835
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(IsolatedTimer, {}) }) })
38557
38836
  ] }),
38558
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(IsolatedTimer, {}) }) })
38559
- ] }),
38560
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-3 py-2 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3 md:gap-4", children: [
38561
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base md:text-lg font-medium text-blue-600", children: /* @__PURE__ */ jsxRuntime.jsx(IsolatedTimer, {}) }),
38562
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-300" }),
38563
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-medium text-blue-600", children: currentDate }),
38564
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-300" }),
38565
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
38566
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-600", children: getShiftIcon(shiftId) }),
38567
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-medium text-blue-600", children: [
38568
- getShiftName(shiftId),
38569
- " Shift"
38837
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-3 py-2 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3 md:gap-4", children: [
38838
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base md:text-lg font-medium text-blue-600", children: /* @__PURE__ */ jsxRuntime.jsx(IsolatedTimer, {}) }),
38839
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-300" }),
38840
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-medium text-blue-600", children: currentDate }),
38841
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-300" }),
38842
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
38843
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-600", children: shiftIcon }),
38844
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-medium text-blue-600", children: [
38845
+ shiftName,
38846
+ " Shift"
38847
+ ] })
38570
38848
  ] })
38571
- ] })
38572
- ] }) })
38573
- ] }));
38849
+ ] }) })
38850
+ ] });
38851
+ });
38574
38852
  HeaderRibbon.displayName = "HeaderRibbon";
38575
38853
  var MobileWorkspaceCard = React21.memo(({
38576
38854
  workspace,
38577
38855
  rank,
38578
38856
  cardClass,
38579
38857
  onWorkspaceClick,
38580
- getMedalIcon,
38581
- getLineName
38858
+ getMedalIcon
38582
38859
  }) => /* @__PURE__ */ jsxRuntime.jsxs(
38583
38860
  "div",
38584
38861
  {
38585
38862
  onClick: () => onWorkspaceClick(workspace, rank),
38586
- className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] transition-all cursor-pointer`,
38863
+ className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] transition-all duration-300 cursor-pointer`,
38864
+ style: {
38865
+ willChange: "opacity, transform",
38866
+ animation: "fadeIn 0.3s ease-in-out"
38867
+ },
38587
38868
  children: [
38588
38869
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
38589
38870
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
@@ -38595,8 +38876,8 @@ var MobileWorkspaceCard = React21.memo(({
38595
38876
  getMedalIcon(rank)
38596
38877
  ] }),
38597
38878
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
38598
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children: getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id) }),
38599
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: getLineName(workspace.line_id) })
38879
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children: workspace.displayName }),
38880
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: workspace.lineName })
38600
38881
  ] })
38601
38882
  ] }),
38602
38883
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
@@ -38626,27 +38907,32 @@ var MobileWorkspaceCard = React21.memo(({
38626
38907
  ] })
38627
38908
  ]
38628
38909
  }
38629
- ));
38910
+ ), (prevProps, nextProps) => {
38911
+ return prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName && prevProps.onWorkspaceClick === nextProps.onWorkspaceClick && prevProps.getMedalIcon === nextProps.getMedalIcon;
38912
+ });
38630
38913
  MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
38631
38914
  var DesktopWorkspaceRow = React21.memo(({
38632
38915
  workspace,
38633
38916
  index,
38634
38917
  rowClass,
38635
38918
  onWorkspaceClick,
38636
- getMedalIcon,
38637
- getLineName
38919
+ getMedalIcon
38638
38920
  }) => /* @__PURE__ */ jsxRuntime.jsxs(
38639
38921
  "tr",
38640
38922
  {
38641
38923
  onClick: () => onWorkspaceClick(workspace, index + 1),
38642
- className: `${rowClass} hover:bg-gray-50/90 transition-colors cursor-pointer group`,
38924
+ className: `${rowClass} hover:bg-gray-50/90 transition-all duration-300 cursor-pointer group`,
38925
+ style: {
38926
+ willChange: "opacity, background-color",
38927
+ animation: "fadeIn 0.3s ease-in-out"
38928
+ },
38643
38929
  children: [
38644
38930
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap group-hover:font-medium", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
38645
38931
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: index + 1 }),
38646
38932
  getMedalIcon(index + 1)
38647
38933
  ] }) }),
38648
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id) }) }),
38649
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: getLineName(workspace.line_id) }) }),
38934
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.displayName }) }),
38935
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.lineName }) }),
38650
38936
  /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium whitespace-nowrap", children: [
38651
38937
  (workspace.efficiency || 0).toFixed(1),
38652
38938
  "%"
@@ -38664,7 +38950,9 @@ var DesktopWorkspaceRow = React21.memo(({
38664
38950
  ] })
38665
38951
  ]
38666
38952
  }
38667
- ));
38953
+ ), (prevProps, nextProps) => {
38954
+ return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName && prevProps.onWorkspaceClick === nextProps.onWorkspaceClick && prevProps.getMedalIcon === nextProps.getMedalIcon;
38955
+ });
38668
38956
  DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
38669
38957
  var LeaderboardDetailView = React21.memo(({
38670
38958
  lineId,
@@ -38680,6 +38968,13 @@ var LeaderboardDetailView = React21.memo(({
38680
38968
  const navigation = useNavigation();
38681
38969
  const entityConfig = useEntityConfig();
38682
38970
  const [sortAscending, setSortAscending] = React21.useState(false);
38971
+ const [isMobile, setIsMobile] = React21.useState(false);
38972
+ React21__namespace.default.useEffect(() => {
38973
+ const checkMobile = () => setIsMobile(window.innerWidth < 640);
38974
+ checkMobile();
38975
+ window.addEventListener("resize", checkMobile);
38976
+ return () => window.removeEventListener("resize", checkMobile);
38977
+ }, []);
38683
38978
  const configuredLineNames = React21.useMemo(() => {
38684
38979
  return getAllLineDisplayNames(entityConfig);
38685
38980
  }, [entityConfig]);
@@ -38697,18 +38992,10 @@ var LeaderboardDetailView = React21.memo(({
38697
38992
  const handleSortToggle = React21.useCallback(() => {
38698
38993
  setSortAscending(!sortAscending);
38699
38994
  }, [sortAscending]);
38700
- const realtimeMetricsParams = React21.useMemo(() => ({
38701
- lineId: lineId || "",
38702
- date,
38703
- shiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0
38704
- }), [lineId, date, shift]);
38705
- const {
38706
- metrics: metrics2,
38707
- lineDetails,
38708
- loading: metricsLoading,
38709
- error: metricsError,
38710
- refreshMetrics
38711
- } = useRealtimeLineMetrics(realtimeMetricsParams);
38995
+ const shiftId = React21.useMemo(
38996
+ () => typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
38997
+ [shift]
38998
+ );
38712
38999
  const {
38713
39000
  workspaces,
38714
39001
  loading: workspacesLoading,
@@ -38718,12 +39005,12 @@ var LeaderboardDetailView = React21.memo(({
38718
39005
  initialDate: date,
38719
39006
  initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0
38720
39007
  });
38721
- const getShiftName = React21.useCallback((shiftId) => {
38722
- if (shiftId === void 0) return "Day";
38723
- return shiftId === 0 ? "Day" : "Night";
39008
+ const getShiftName = React21.useCallback((shiftId2) => {
39009
+ if (shiftId2 === void 0) return "Day";
39010
+ return shiftId2 === 0 ? "Day" : "Night";
38724
39011
  }, []);
38725
- const getShiftIcon = React21.useCallback((shiftId) => {
38726
- const shift2 = getShiftName(shiftId);
39012
+ const getShiftIcon = React21.useCallback((shiftId2) => {
39013
+ const shift2 = getShiftName(shiftId2);
38727
39014
  if (shift2 === "Day") {
38728
39015
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
38729
39016
  } else {
@@ -38765,17 +39052,18 @@ var LeaderboardDetailView = React21.memo(({
38765
39052
  return null;
38766
39053
  }
38767
39054
  }, [sortAscending]);
39055
+ const workspacesLength = React21.useMemo(() => workspaces?.length || 0, [workspaces?.length]);
38768
39056
  const handleWorkspaceClick = React21.useCallback((workspace, rank) => {
38769
39057
  trackCoreEvent("Workspace from Leaderboard Clicked", {
38770
39058
  workspace_name: workspace.workspace_name,
38771
39059
  workspace_id: workspace.workspace_uuid,
38772
39060
  rank,
38773
- total_workspaces: workspaces?.length || 0,
39061
+ total_workspaces: workspacesLength,
38774
39062
  efficiency: workspace.efficiency,
38775
39063
  action_count: workspace.action_count,
38776
39064
  action_threshold: workspace.action_threshold
38777
39065
  });
38778
- const displayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
39066
+ const displayName = workspace.displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
38779
39067
  const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName, workspace.line_id) : "";
38780
39068
  const returnToParam = `&returnTo=${encodeURIComponent(`/leaderboard`)}`;
38781
39069
  if (onWorkspaceClick) {
@@ -38783,20 +39071,33 @@ var LeaderboardDetailView = React21.memo(({
38783
39071
  } else {
38784
39072
  navigation.navigate(`/workspace/${workspace.workspace_uuid}${navParams}${returnToParam}`);
38785
39073
  }
38786
- }, [onWorkspaceClick, navigation, lineId, workspaces?.length]);
38787
- const sortedWorkspaces = React21.useMemo(() => {
39074
+ }, [onWorkspaceClick, navigation, lineId, workspacesLength]);
39075
+ const workspaceDisplayData = React21.useMemo(() => {
38788
39076
  if (!workspaces) return [];
38789
- return [...workspaces].sort((a, b) => {
39077
+ return workspaces.map((ws) => ({
39078
+ ...ws,
39079
+ displayName: getWorkspaceDisplayName(ws.workspace_name, ws.line_id),
39080
+ lineName: getLineName(ws.line_id)
39081
+ }));
39082
+ }, [workspaces, getLineName]);
39083
+ const sortedWorkspaces = React21.useMemo(() => {
39084
+ return [...workspaceDisplayData].sort((a, b) => {
38790
39085
  const effA = a.efficiency || 0;
38791
39086
  const effB = b.efficiency || 0;
38792
39087
  return sortAscending ? effA - effB : effB - effA;
38793
39088
  });
38794
- }, [workspaces, sortAscending]);
38795
- const loading = React21.useMemo(() => metricsLoading || workspacesLoading, [metricsLoading, workspacesLoading]);
38796
- const error = React21.useMemo(() => metricsError || workspacesError, [metricsError, workspacesError]);
38797
- const currentDateFormatted = React21.useMemo(() => formatDate(/* @__PURE__ */ new Date()), [formatDate]);
38798
- const currentMobileDateFormatted = React21.useMemo(() => formatMobileDate(/* @__PURE__ */ new Date()), [formatMobileDate]);
38799
- if (loading) {
39089
+ }, [workspaceDisplayData, sortAscending]);
39090
+ const loading = workspacesLoading;
39091
+ const error = workspacesError;
39092
+ const currentDateFormatted = React21.useMemo(() => {
39093
+ const dateStr = (/* @__PURE__ */ new Date()).toDateString();
39094
+ return formatDate(new Date(dateStr));
39095
+ }, [formatDate, date]);
39096
+ const currentMobileDateFormatted = React21.useMemo(() => {
39097
+ const dateStr = (/* @__PURE__ */ new Date()).toDateString();
39098
+ return formatMobileDate(new Date(dateStr));
39099
+ }, [formatMobileDate, date]);
39100
+ if (loading && (!workspaces || workspaces.length === 0)) {
38800
39101
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `h-[calc(100vh-64px)] flex items-center justify-center bg-slate-50 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl text-gray-600", children: "Loading workspaces..." }) });
38801
39102
  }
38802
39103
  if (error) {
@@ -38805,7 +39106,7 @@ var LeaderboardDetailView = React21.memo(({
38805
39106
  error.message
38806
39107
  ] }) });
38807
39108
  }
38808
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, children: [
39109
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, style: { willChange: "contents" }, children: [
38809
39110
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-20 bg-white shadow-sm border-b border-gray-200/80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-6 md:px-8 py-2 sm:py-2.5", children: [
38810
39111
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
38811
39112
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -38897,31 +39198,30 @@ var LeaderboardDetailView = React21.memo(({
38897
39198
  {
38898
39199
  currentDate: currentDateFormatted,
38899
39200
  currentMobileDate: currentMobileDateFormatted,
38900
- shiftId: metrics2?.shift_id,
39201
+ shiftId,
38901
39202
  getShiftIcon,
38902
39203
  getShiftName
38903
39204
  }
38904
39205
  )
38905
39206
  ] }) }),
38906
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 w-full mx-auto p-3 sm:p-4 md:p-6", children: [
38907
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden space-y-3", children: sortedWorkspaces.map((ws, index) => {
38908
- const rank = index + 1;
38909
- const isTopThree = index < 3;
38910
- const cardClass = sortAscending ? isTopThree ? "bg-red-50/90 border-red-200" : "bg-white" : isTopThree ? "bg-green-50/90 border-green-200" : "bg-white";
38911
- return /* @__PURE__ */ jsxRuntime.jsx(
38912
- MobileWorkspaceCard,
38913
- {
38914
- workspace: ws,
38915
- rank,
38916
- cardClass,
38917
- onWorkspaceClick: handleWorkspaceClick,
38918
- getMedalIcon,
38919
- getLineName
38920
- },
38921
- ws.workspace_uuid
38922
- );
38923
- }) }),
38924
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block bg-white rounded-lg shadow-md overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse table-auto", children: [
39207
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full mx-auto p-3 sm:p-4 md:p-6", children: isMobile ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: sortedWorkspaces.map((ws, index) => {
39208
+ const rank = index + 1;
39209
+ const isTopThree = index < 3;
39210
+ const cardClass = sortAscending ? isTopThree ? "bg-red-50/90 border-red-200" : "bg-white" : isTopThree ? "bg-green-50/90 border-green-200" : "bg-white";
39211
+ return /* @__PURE__ */ jsxRuntime.jsx(
39212
+ MobileWorkspaceCard,
39213
+ {
39214
+ workspace: ws,
39215
+ rank,
39216
+ cardClass,
39217
+ onWorkspaceClick: handleWorkspaceClick,
39218
+ getMedalIcon
39219
+ },
39220
+ ws.workspace_uuid
39221
+ );
39222
+ }) }) : (
39223
+ /* Desktop table view - only render on desktop */
39224
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-md overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse table-auto", children: [
38925
39225
  /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-50 border-b border-gray-200 sticky top-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
38926
39226
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Rank" }),
38927
39227
  /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Workspace" }),
@@ -38940,17 +39240,16 @@ var LeaderboardDetailView = React21.memo(({
38940
39240
  index,
38941
39241
  rowClass,
38942
39242
  onWorkspaceClick: handleWorkspaceClick,
38943
- getMedalIcon,
38944
- getLineName
39243
+ getMedalIcon
38945
39244
  },
38946
39245
  ws.workspace_uuid
38947
39246
  );
38948
39247
  }) })
38949
39248
  ] }) }) })
38950
- ] })
39249
+ ) })
38951
39250
  ] });
38952
39251
  }, (prevProps, nextProps) => {
38953
- return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && JSON.stringify(prevProps.lineNames) === JSON.stringify(nextProps.lineNames) && prevProps.className === nextProps.className && prevProps.onBackClick === nextProps.onBackClick && prevProps.onWorkspaceClick === nextProps.onWorkspaceClick;
39252
+ return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && JSON.stringify(prevProps.lineNames) === JSON.stringify(nextProps.lineNames) && prevProps.className === nextProps.className;
38954
39253
  });
38955
39254
  LeaderboardDetailView.displayName = "LeaderboardDetailView";
38956
39255
  var LeaderboardDetailViewWithDisplayNames = withAllWorkspaceDisplayNames(LeaderboardDetailView);
@@ -40059,16 +40358,8 @@ var ACTION_NAMES = {
40059
40358
  // src/views/TargetsView.utils.ts
40060
40359
  var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
40061
40360
  if (cycleTime === "" || cycleTime === 0) return "";
40062
- const basicPPH = 3600 / cycleTime;
40063
- if (breaks.length === 0 || shiftHours === 0) {
40064
- return Number(basicPPH.toFixed(1));
40065
- }
40066
- const safeBreaks = Array.isArray(breaks) ? breaks : [];
40067
- const totalBreakMinutes = safeBreaks.reduce((total, breakItem) => total + breakItem.duration, 0);
40068
- const totalBreakHours = totalBreakMinutes / 60;
40069
- const realWorkHours = shiftHours - totalBreakHours;
40070
- const effectivePPH = basicPPH * (realWorkHours / shiftHours);
40071
- return Number(effectivePPH.toFixed(1));
40361
+ const pph = 3600 / cycleTime;
40362
+ return Number(pph.toFixed(1));
40072
40363
  };
40073
40364
  var calculateDayOutput = (pph, shiftHours, breaks = []) => {
40074
40365
  if (pph === "") return "";
@@ -42664,7 +42955,7 @@ var WorkspaceDetailView = ({
42664
42955
  /* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
42665
42956
  /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
42666
42957
  /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
42667
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
42958
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
42668
42959
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
42669
42960
  "Standard: ",
42670
42961
  workspace.ideal_cycle_time?.toFixed(1) || 0,
@@ -42784,7 +43075,7 @@ var WorkspaceDetailView = ({
42784
43075
  /* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
42785
43076
  /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
42786
43077
  /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
42787
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
43078
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
42788
43079
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
42789
43080
  "Standard: ",
42790
43081
  workspace.ideal_cycle_time?.toFixed(1) || 0,
@@ -44124,6 +44415,7 @@ exports.AuthenticatedHomeView = AuthenticatedHomeView;
44124
44415
  exports.AuthenticatedShiftsView = AuthenticatedShiftsView;
44125
44416
  exports.AuthenticatedTargetsView = AuthenticatedTargetsView;
44126
44417
  exports.AuthenticatedWorkspaceHealthView = AuthenticatedWorkspaceHealthView;
44418
+ exports.AxelNotificationPopup = AxelNotificationPopup;
44127
44419
  exports.BackButton = BackButton;
44128
44420
  exports.BackButtonMinimal = BackButtonMinimal;
44129
44421
  exports.BarChart = BarChart;