@optifye/dashboard-core 6.11.24 → 6.11.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2987,6 +2987,7 @@ var AuthService = class {
2987
2987
  scope_ttl_seconds: enrichedUser.scope_ttl_seconds,
2988
2988
  access_scope: enrichedUser.access_scope,
2989
2989
  company_id: enrichedUser.company_id,
2990
+ company: enrichedUser.company,
2990
2991
  first_login_completed: enrichedUser.profile.first_login_completed,
2991
2992
  properties: {
2992
2993
  company_id: enrichedUser.company_id,
@@ -13255,7 +13256,7 @@ var parseEfficiencyLegend = (legend) => {
13255
13256
  critical_threshold: coerce(legend.critical_threshold, DEFAULT_EFFICIENCY_LEGEND.critical_threshold)
13256
13257
  };
13257
13258
  };
13258
- var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds }) => {
13259
+ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, lineIds, userAccessibleLineIds }) => {
13259
13260
  const { supabaseUrl, supabaseKey } = useDashboardConfig();
13260
13261
  const entityConfig = useEntityConfig();
13261
13262
  const databaseConfig = useDatabaseConfig();
@@ -13272,9 +13273,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13272
13273
  return getConfiguredLineIds(entityConfig);
13273
13274
  }, [entityConfig]);
13274
13275
  const targetFactoryLineIds = useMemo(() => {
13275
- const sourceLineIds = userAccessibleLineIds !== void 0 ? userAccessibleLineIds : configuredLineIds;
13276
+ const sourceLineIds = lineIds !== void 0 ? lineIds : userAccessibleLineIds !== void 0 ? userAccessibleLineIds : configuredLineIds;
13276
13277
  return Array.from(new Set((sourceLineIds || []).filter(Boolean)));
13277
- }, [userAccessibleLineIds, configuredLineIds]);
13278
+ }, [lineIds, userAccessibleLineIds, configuredLineIds]);
13278
13279
  const { shiftConfig: staticShiftConfig } = useDashboardConfig();
13279
13280
  const {
13280
13281
  shiftConfigMap: multiLineShiftConfigMap,
@@ -13327,6 +13328,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13327
13328
  const operationalShiftKeyRef = useRef(operationalShiftKey);
13328
13329
  const configuredLineIdsRef = useRef(configuredLineIds);
13329
13330
  const userAccessibleLineIdsRef = useRef(userAccessibleLineIds);
13331
+ const explicitLineIdsRef = useRef(lineIds);
13330
13332
  useEffect(() => {
13331
13333
  onLineMetricsUpdateRef.current = onLineMetricsUpdate;
13332
13334
  }, [onLineMetricsUpdate]);
@@ -13342,6 +13344,9 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13342
13344
  useEffect(() => {
13343
13345
  userAccessibleLineIdsRef.current = userAccessibleLineIds;
13344
13346
  }, [userAccessibleLineIds]);
13347
+ useEffect(() => {
13348
+ explicitLineIdsRef.current = lineIds;
13349
+ }, [lineIds]);
13345
13350
  const companySpecificMetricsTable = useMemo(
13346
13351
  () => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
13347
13352
  [entityConfig.companyId]
@@ -13692,6 +13697,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13692
13697
  isFactoryView,
13693
13698
  multiLineShiftConfigMap,
13694
13699
  staticShiftConfig,
13700
+ lineIds,
13695
13701
  userAccessibleLineIds,
13696
13702
  effectiveWorkspaceConfig,
13697
13703
  shiftLoading,
@@ -13809,7 +13815,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13809
13815
  logDebug("[useDashboardMetrics] Setting up group subscriptions:", {
13810
13816
  groupCount: currentShiftGroups.length
13811
13817
  });
13812
- const targetFactoryLineIdsForSubscriptions = currentUserAccessibleLineIds !== void 0 ? currentUserAccessibleLineIds : currentConfiguredLineIds;
13818
+ const targetFactoryLineIdsForSubscriptions = explicitLineIdsRef.current !== void 0 ? explicitLineIdsRef.current : currentUserAccessibleLineIds !== void 0 ? currentUserAccessibleLineIds : currentConfiguredLineIds;
13813
13819
  const targetFactoryLineIdSet = new Set(
13814
13820
  (targetFactoryLineIdsForSubscriptions || []).filter(Boolean)
13815
13821
  );
@@ -13980,7 +13986,7 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId, userAccessibleLineIds
13980
13986
  });
13981
13987
  const currentShiftDetails = shiftConfig ? getCurrentShift(defaultTimezone, shiftConfig) : getCurrentShift(defaultTimezone, staticShiftConfig);
13982
13988
  const operationalDateForSubscription = currentShiftDetails.date;
13983
- const targetLineIds = isFactory ? currentUserAccessibleLineIds || currentConfiguredLineIds : [currentLineIdToUse];
13989
+ const targetLineIds = isFactory ? explicitLineIdsRef.current || currentUserAccessibleLineIds || currentConfiguredLineIds : [currentLineIdToUse];
13984
13990
  const filteredLineIds = targetLineIds.filter((id3) => id3 && id3 !== factoryViewIdentifier);
13985
13991
  if (filteredLineIds.length === 0) {
13986
13992
  logDebug("[useDashboardMetrics] Realtime setup skipped: no line IDs after filtering", {
@@ -20222,6 +20228,17 @@ function useCompanyHasVlmEnabledLine(options = {}) {
20222
20228
  };
20223
20229
  }
20224
20230
 
20231
+ // src/lib/hooks/useCompanyFastSlowClipFiltersEnabled.ts
20232
+ function useCompanyFastSlowClipFiltersEnabled() {
20233
+ const { user, loading } = useAuth();
20234
+ const company = user?.company || user?.properties?.company;
20235
+ const explicitValue = company?.enable_fast_slow_clip_filters;
20236
+ return {
20237
+ isFastSlowClipFiltersEnabled: typeof explicitValue === "boolean" ? explicitValue : true,
20238
+ isResolved: !loading || typeof explicitValue === "boolean"
20239
+ };
20240
+ }
20241
+
20225
20242
  // src/lib/utils/api.ts
20226
20243
  var apiUtils = {
20227
20244
  /**
@@ -20394,13 +20411,15 @@ var buildKPIsFromLineMetricsRow = (row) => {
20394
20411
  };
20395
20412
  var aggregateKPIsFromLineMetricsRows = (rows) => {
20396
20413
  if (!rows || rows.length === 0) return createDefaultKPIs();
20397
- const currentOutputSum = rows.reduce((sum, row) => sum + toNumber(row.current_output), 0);
20398
- const lineThresholdSum = rows.reduce((sum, row) => sum + toNumber(row.line_threshold), 0);
20399
- const idealOutputSum = rows.reduce(
20414
+ const eligibleRows = rows.filter((row) => toNumber(row?.avg_efficiency) >= 5);
20415
+ if (eligibleRows.length === 0) return createDefaultKPIs();
20416
+ const currentOutputSum = eligibleRows.reduce((sum, row) => sum + toNumber(row.current_output), 0);
20417
+ const lineThresholdSum = eligibleRows.reduce((sum, row) => sum + toNumber(row.line_threshold), 0);
20418
+ const idealOutputSum = eligibleRows.reduce(
20400
20419
  (sum, row) => sum + (toNumber(row.ideal_output) || toNumber(row.line_threshold)),
20401
20420
  0
20402
20421
  );
20403
- const efficiencyValues = rows.map((row) => {
20422
+ const efficiencyValues = eligibleRows.map((row) => {
20404
20423
  const value = row?.avg_efficiency;
20405
20424
  if (typeof value === "number" && Number.isFinite(value)) return value;
20406
20425
  if (typeof value === "string" && value.trim() !== "") {
@@ -20410,10 +20429,10 @@ var aggregateKPIsFromLineMetricsRows = (rows) => {
20410
20429
  return null;
20411
20430
  }).filter((value) => value !== null);
20412
20431
  const avgEfficiency = efficiencyValues.length > 0 ? efficiencyValues.reduce((sum, value) => sum + value, 0) / efficiencyValues.length : 0;
20413
- const numLines = rows.length;
20414
- const avgCycleTime = numLines > 0 ? rows.reduce((sum, row) => sum + toNumber(row.avg_cycle_time), 0) / numLines : 0;
20415
- const totalUnderperforming = rows.reduce((sum, row) => sum + toNumber(row.underperforming_workspaces), 0);
20416
- const totalWorkspaces = rows.reduce((sum, row) => sum + toNumber(row.total_workspaces), 0);
20432
+ const numLines = eligibleRows.length;
20433
+ const avgCycleTime = numLines > 0 ? eligibleRows.reduce((sum, row) => sum + toNumber(row.avg_cycle_time), 0) / numLines : 0;
20434
+ const totalUnderperforming = eligibleRows.reduce((sum, row) => sum + toNumber(row.underperforming_workspaces), 0);
20435
+ const totalWorkspaces = eligibleRows.reduce((sum, row) => sum + toNumber(row.total_workspaces), 0);
20417
20436
  return {
20418
20437
  underperformingWorkers: {
20419
20438
  current: totalUnderperforming,
@@ -34569,6 +34588,8 @@ var logDebug2 = (...args) => {
34569
34588
  var VideoGridView = React142__default.memo(({
34570
34589
  workspaces,
34571
34590
  selectedLine,
34591
+ lineNames = {},
34592
+ lineOrder = [],
34572
34593
  className = "",
34573
34594
  legend,
34574
34595
  videoSources = {},
@@ -34671,6 +34692,44 @@ var VideoGridView = React142__default.memo(({
34671
34692
  }
34672
34693
  }) : workspaces;
34673
34694
  }, [workspaces, selectedLine]);
34695
+ const sortedWorkspaces = useMemo(() => {
34696
+ return [...filteredWorkspaces].sort((a, b) => {
34697
+ if (a.line_id !== b.line_id) {
34698
+ return (a.line_id || "").localeCompare(b.line_id || "");
34699
+ }
34700
+ const aMatch = a.workspace_name.match(/WS(\d+)/);
34701
+ const bMatch = b.workspace_name.match(/WS(\d+)/);
34702
+ if (aMatch && bMatch) {
34703
+ const aNum = parseInt(aMatch[1], 10);
34704
+ const bNum = parseInt(bMatch[1], 10);
34705
+ return aNum - bNum;
34706
+ }
34707
+ return a.workspace_name.localeCompare(b.workspace_name);
34708
+ });
34709
+ }, [filteredWorkspaces]);
34710
+ const lineGroups = useMemo(() => {
34711
+ const grouped = /* @__PURE__ */ new Map();
34712
+ sortedWorkspaces.forEach((workspace) => {
34713
+ const lineId = workspace.line_id || "unknown";
34714
+ const existing = grouped.get(lineId);
34715
+ if (existing) {
34716
+ existing.push(workspace);
34717
+ return;
34718
+ }
34719
+ grouped.set(lineId, [workspace]);
34720
+ });
34721
+ const sortedRemainingLineIds = Array.from(grouped.keys()).sort((a, b) => a.localeCompare(b));
34722
+ const orderedLineIds = [
34723
+ ...lineOrder.filter((lineId) => grouped.has(lineId)),
34724
+ ...sortedRemainingLineIds.filter((lineId) => !lineOrder.includes(lineId))
34725
+ ];
34726
+ return orderedLineIds.map((lineId) => ({
34727
+ lineId,
34728
+ lineName: lineNames[lineId] || `Line ${lineId.substring(0, 4)}`,
34729
+ workspaces: grouped.get(lineId) || []
34730
+ }));
34731
+ }, [sortedWorkspaces, lineOrder, lineNames]);
34732
+ lineGroups.length > 1;
34674
34733
  const streamsReady = !videoStreamsLoading;
34675
34734
  const calculateOptimalGrid = useCallback(() => {
34676
34735
  if (!containerRef.current) return;
@@ -34834,108 +34893,130 @@ var VideoGridView = React142__default.memo(({
34834
34893
  stream_source: isR2Stream ? "r2" : "media_config"
34835
34894
  });
34836
34895
  }, []);
34896
+ const workspaceCards = useMemo(() => {
34897
+ return sortedWorkspaces.map((workspace) => {
34898
+ const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
34899
+ const workspaceKey = `${workspace.line_id || "unknown"}-${workspaceId}`;
34900
+ const isVisible = visibleWorkspaces.has(workspaceId);
34901
+ const isVeryLowEfficiency = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
34902
+ const workspaceCropping = getWorkspaceCropping(workspaceId, workspace.workspace_name);
34903
+ const workspaceStream = videoStreamsByWorkspaceId?.[workspaceId];
34904
+ const lastSeenLabel = workspace.workspace_uuid ? lastSeenByWorkspaceId[workspace.workspace_uuid]?.timeSinceLastUpdate : void 0;
34905
+ const r2Url = workspaceStream?.hls_url;
34906
+ const fallbackUrl = getWorkspaceHlsUrl(workspace.workspace_name, workspace.line_id);
34907
+ const hasR2Stream = Boolean(r2Url);
34908
+ const useFallback = r2FallbackWorkspaces.has(workspaceId) || streamsReady && !hasR2Stream;
34909
+ const hlsUrl = useFallback ? fallbackUrl : r2Url ?? "";
34910
+ const isR2Stream = !useFallback && hasR2Stream;
34911
+ const canAttemptR2 = hasR2Stream && !r2FallbackWorkspaces.has(workspaceId);
34912
+ const shouldPlay = isVisible && Boolean(hlsUrl) && (!failedStreams.has(workspaceId) || canAttemptR2);
34913
+ return {
34914
+ workspace,
34915
+ workspaceId,
34916
+ workspaceKey,
34917
+ isVisible,
34918
+ isVeryLowEfficiency,
34919
+ workspaceCropping,
34920
+ fallbackUrl,
34921
+ hlsUrl,
34922
+ isR2Stream,
34923
+ shouldPlay,
34924
+ lastSeenLabel
34925
+ };
34926
+ });
34927
+ }, [
34928
+ sortedWorkspaces,
34929
+ visibleWorkspaces,
34930
+ getWorkspaceCropping,
34931
+ videoStreamsByWorkspaceId,
34932
+ lastSeenByWorkspaceId,
34933
+ getWorkspaceHlsUrl,
34934
+ r2FallbackWorkspaces,
34935
+ streamsReady,
34936
+ failedStreams
34937
+ ]);
34938
+ useMemo(() => {
34939
+ const map = /* @__PURE__ */ new Map();
34940
+ workspaceCards.forEach((card) => {
34941
+ map.set(card.workspaceKey, card);
34942
+ });
34943
+ return map;
34944
+ }, [workspaceCards]);
34945
+ const croppedActiveCount = useMemo(() => {
34946
+ return workspaceCards.reduce((count, card) => {
34947
+ if (card.shouldPlay && card.workspaceCropping) {
34948
+ return count + 1;
34949
+ }
34950
+ return count;
34951
+ }, 0);
34952
+ }, [workspaceCards]);
34953
+ const throttleCropping = croppedActiveCount > 10;
34954
+ const effectiveCanvasFps = throttleCropping ? 10 : canvasConfig?.fps;
34955
+ const effectiveUseRAF = throttleCropping ? false : canvasConfig?.useRAF;
34837
34956
  const displayMinuteBucket = Math.floor(Date.now() / 6e4);
34838
- return /* @__PURE__ */ jsx("div", { className: `relative overflow-hidden h-full w-full ${className}`, children: /* @__PURE__ */ jsx("div", { ref: containerRef, className: "h-full w-full p-3 sm:p-2", children: /* @__PURE__ */ jsx(
34957
+ const renderWorkspaceCard = useCallback((card, className2) => /* @__PURE__ */ jsx(
34839
34958
  "div",
34840
34959
  {
34841
- className: "grid h-full w-full gap-3 sm:gap-2",
34842
- style: {
34843
- gridTemplateColumns: `repeat(${gridCols}, 1fr)`,
34844
- gridTemplateRows: `repeat(${gridRows}, 1fr)`,
34845
- gridAutoFlow: "row"
34846
- },
34847
- children: (() => {
34848
- const sortedWorkspaces = [...filteredWorkspaces].sort((a, b) => {
34849
- if (a.line_id !== b.line_id) {
34850
- return (a.line_id || "").localeCompare(b.line_id || "");
34851
- }
34852
- const aMatch = a.workspace_name.match(/WS(\d+)/);
34853
- const bMatch = b.workspace_name.match(/WS(\d+)/);
34854
- if (aMatch && bMatch) {
34855
- const aNum = parseInt(aMatch[1]);
34856
- const bNum = parseInt(bMatch[1]);
34857
- return aNum - bNum;
34858
- }
34859
- return a.workspace_name.localeCompare(b.workspace_name);
34860
- });
34861
- const workspaceCards = sortedWorkspaces.map((workspace) => {
34862
- const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
34863
- `${workspace.line_id || "unknown"}-${workspaceId}`;
34864
- const isVisible = visibleWorkspaces.has(workspaceId);
34865
- const isVeryLowEfficiency = workspace.show_exclamation ?? (workspace.efficiency < 50 && workspace.efficiency >= 10);
34866
- const workspaceCropping = getWorkspaceCropping(workspaceId, workspace.workspace_name);
34867
- const workspaceStream = videoStreamsByWorkspaceId?.[workspaceId];
34868
- const lastSeenLabel = workspace.workspace_uuid ? lastSeenByWorkspaceId[workspace.workspace_uuid]?.timeSinceLastUpdate : void 0;
34869
- const r2Url = workspaceStream?.hls_url;
34870
- const fallbackUrl = getWorkspaceHlsUrl(workspace.workspace_name, workspace.line_id);
34871
- const hasR2Stream = Boolean(r2Url);
34872
- const useFallback = r2FallbackWorkspaces.has(workspaceId) || streamsReady && !hasR2Stream;
34873
- const hlsUrl = useFallback ? fallbackUrl : r2Url ?? "";
34874
- const isR2Stream = !useFallback && hasR2Stream;
34875
- const canAttemptR2 = hasR2Stream && !r2FallbackWorkspaces.has(workspaceId);
34876
- const shouldPlay = isVisible && Boolean(hlsUrl) && (!failedStreams.has(workspaceId) || canAttemptR2);
34877
- return {
34878
- workspace,
34879
- workspaceId,
34880
- isVisible,
34881
- isVeryLowEfficiency,
34882
- workspaceCropping,
34883
- fallbackUrl,
34884
- hlsUrl,
34885
- isR2Stream,
34886
- shouldPlay,
34887
- lastSeenLabel
34888
- };
34889
- });
34890
- const croppedActiveCount = workspaceCards.reduce((count, card) => {
34891
- if (card.shouldPlay && card.workspaceCropping) {
34892
- return count + 1;
34893
- }
34894
- return count;
34895
- }, 0);
34896
- const throttleCropping = croppedActiveCount > 10;
34897
- const effectiveCanvasFps = throttleCropping ? 10 : canvasConfig?.fps;
34898
- const effectiveUseRAF = throttleCropping ? false : canvasConfig?.useRAF;
34899
- return workspaceCards.map((card) => /* @__PURE__ */ jsx(
34900
- "div",
34901
- {
34902
- "data-workspace-id": card.workspaceId,
34903
- className: "workspace-card relative w-full h-full",
34904
- children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx(
34905
- VideoCard,
34906
- {
34907
- workspace: card.workspace,
34908
- hlsUrl: card.hlsUrl,
34909
- shouldPlay: card.shouldPlay,
34910
- onClick: () => handleWorkspaceClick(card.workspace),
34911
- onFatalError: () => handleStreamError(card.workspaceId, {
34912
- isR2Stream: card.isR2Stream,
34913
- fallbackUrl: card.fallbackUrl
34914
- }),
34915
- isVeryLowEfficiency: card.isVeryLowEfficiency,
34916
- legend: effectiveLegend,
34917
- cropping: card.workspaceCropping,
34918
- canvasFps: effectiveCanvasFps,
34919
- displayName: (
34920
- // Create line-aware lookup key: lineId_workspaceName
34921
- // This ensures correct mapping when multiple lines have same workspace names
34922
- displayNames[`${card.workspace.line_id}_${card.workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
34923
- getWorkspaceDisplayName(card.workspace.workspace_name, card.workspace.line_id)
34924
- ),
34925
- lastSeenLabel: card.lastSeenLabel,
34926
- useRAF: effectiveUseRAF,
34927
- displayMinuteBucket,
34928
- compact: !selectedLine,
34929
- onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(card.workspaceId) : void 0,
34930
- onMouseLeave: onWorkspaceHoverEnd ? () => onWorkspaceHoverEnd(card.workspaceId) : void 0
34931
- }
34932
- ) })
34960
+ "data-workspace-id": card.workspaceId,
34961
+ className: className2,
34962
+ children: /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children: /* @__PURE__ */ jsx(
34963
+ VideoCard,
34964
+ {
34965
+ workspace: card.workspace,
34966
+ hlsUrl: card.hlsUrl,
34967
+ shouldPlay: card.shouldPlay,
34968
+ onClick: () => handleWorkspaceClick(card.workspace),
34969
+ onFatalError: () => handleStreamError(card.workspaceId, {
34970
+ isR2Stream: card.isR2Stream,
34971
+ fallbackUrl: card.fallbackUrl
34972
+ }),
34973
+ isVeryLowEfficiency: card.isVeryLowEfficiency,
34974
+ legend: effectiveLegend,
34975
+ cropping: card.workspaceCropping,
34976
+ canvasFps: effectiveCanvasFps,
34977
+ displayName: displayNames[`${card.workspace.line_id}_${card.workspace.workspace_name}`] || getWorkspaceDisplayName(card.workspace.workspace_name, card.workspace.line_id),
34978
+ lastSeenLabel: card.lastSeenLabel,
34979
+ useRAF: effectiveUseRAF,
34980
+ displayMinuteBucket,
34981
+ compact: !selectedLine,
34982
+ onMouseEnter: onWorkspaceHover ? () => onWorkspaceHover(card.workspaceId) : void 0,
34983
+ onMouseLeave: onWorkspaceHoverEnd ? () => onWorkspaceHoverEnd(card.workspaceId) : void 0
34984
+ }
34985
+ ) })
34986
+ },
34987
+ card.workspaceKey
34988
+ ), [
34989
+ displayNames,
34990
+ displayMinuteBucket,
34991
+ effectiveCanvasFps,
34992
+ effectiveLegend,
34993
+ effectiveUseRAF,
34994
+ getWorkspaceDisplayName,
34995
+ handleStreamError,
34996
+ handleWorkspaceClick,
34997
+ onWorkspaceHover,
34998
+ onWorkspaceHoverEnd,
34999
+ selectedLine
35000
+ ]);
35001
+ return /* @__PURE__ */ jsx("div", { className: `relative overflow-hidden h-full w-full bg-slate-50/30 ${className}`, children: /* @__PURE__ */ jsx(
35002
+ "div",
35003
+ {
35004
+ ref: containerRef,
35005
+ className: "h-full w-full px-1 sm:px-2 py-1 sm:py-2",
35006
+ children: /* @__PURE__ */ jsx(
35007
+ "div",
35008
+ {
35009
+ className: "grid h-full w-full gap-1.5 sm:gap-2",
35010
+ style: {
35011
+ gridTemplateColumns: `repeat(${gridCols}, 1fr)`,
35012
+ gridTemplateRows: `repeat(${gridRows}, 1fr)`,
35013
+ gridAutoFlow: "row"
34933
35014
  },
34934
- card.workspaceId
34935
- ));
34936
- })()
35015
+ children: workspaceCards.map((card) => renderWorkspaceCard(card, "workspace-card relative w-full h-full"))
35016
+ }
35017
+ )
34937
35018
  }
34938
- ) }) });
35019
+ ) });
34939
35020
  });
34940
35021
  VideoGridView.displayName = "VideoGridView";
34941
35022
  var MapGridView = React142__default.memo(({
@@ -38128,14 +38209,15 @@ var HlsVideoPlayer = forwardRef(({
38128
38209
  };
38129
38210
  }, [effectiveSrc, initializePlayer, cleanupBlobUrl]);
38130
38211
  useEffect(() => {
38131
- if (videoRef.current) {
38132
- if (autoplay) {
38133
- videoRef.current.play().catch((err) => {
38134
- console.warn("[HlsVideoPlayer] Autoplay failed:", err);
38135
- });
38136
- }
38212
+ if (!videoRef.current || !autoplay || !effectiveSrc) {
38213
+ return;
38137
38214
  }
38138
- }, [autoplay]);
38215
+ videoRef.current.play().catch((err) => {
38216
+ if (err?.name !== "AbortError") {
38217
+ console.warn("[HlsVideoPlayer] Autoplay failed:", err);
38218
+ }
38219
+ });
38220
+ }, [autoplay, effectiveSrc]);
38139
38221
  const resetControlsTimeout = useCallback(() => {
38140
38222
  if (controlsPinned) {
38141
38223
  setShowControls(true);
@@ -40056,7 +40138,8 @@ var FileManagerFilters = ({
40056
40138
  snapshotClipId,
40057
40139
  className = "",
40058
40140
  targetCycleTime = null,
40059
- idleTimeVlmEnabled = false
40141
+ idleTimeVlmEnabled = false,
40142
+ showPercentileCycleFilters = true
40060
40143
  }) => {
40061
40144
  const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
40062
40145
  const [startTime, setStartTime] = useState("");
@@ -40335,6 +40418,9 @@ var FileManagerFilters = ({
40335
40418
  }
40336
40419
  }, [ensureAllIdleTimeClipMetadataLoaded]);
40337
40420
  const fetchPercentileClips = useCallback(async (type) => {
40421
+ if (!showPercentileCycleFilters && type !== "idle-times") {
40422
+ return;
40423
+ }
40338
40424
  if (!workspaceId || !date || shift === void 0) {
40339
40425
  console.warn("[FileManager] Missing required params for percentile clips fetch");
40340
40426
  return;
@@ -40381,7 +40467,7 @@ var FileManagerFilters = ({
40381
40467
  } catch (error) {
40382
40468
  console.error(`[FileManager] Error fetching ${type} clips:`, error);
40383
40469
  }
40384
- }, [workspaceId, date, shift, filterState.percentile, supabase]);
40470
+ }, [workspaceId, date, shift, filterState.percentile, showPercentileCycleFilters, supabase]);
40385
40471
  const percentileCountsKey = useMemo(() => {
40386
40472
  if (!workspaceId || !date || shift === void 0) {
40387
40473
  return null;
@@ -40389,7 +40475,7 @@ var FileManagerFilters = ({
40389
40475
  return `${workspaceId}:${date}:${shift}:${filterState.percentile}`;
40390
40476
  }, [workspaceId, date, shift, filterState.percentile]);
40391
40477
  useEffect(() => {
40392
- if (!percentileCountsKey) {
40478
+ if (!showPercentileCycleFilters || !percentileCountsKey) {
40393
40479
  percentileCountsKeyRef.current = null;
40394
40480
  percentilePrefetchRef.current = { key: null, types: /* @__PURE__ */ new Set() };
40395
40481
  setPercentileCounts({
@@ -40409,8 +40495,11 @@ var FileManagerFilters = ({
40409
40495
  "slow-cycles": null
40410
40496
  });
40411
40497
  setPercentileClips({});
40412
- }, [percentileCountsKey]);
40498
+ }, [showPercentileCycleFilters, percentileCountsKey]);
40413
40499
  useEffect(() => {
40500
+ if (!showPercentileCycleFilters) {
40501
+ return;
40502
+ }
40414
40503
  if (!prefetchedPercentileCounts || !percentileCountsKey) {
40415
40504
  return;
40416
40505
  }
@@ -40425,8 +40514,11 @@ var FileManagerFilters = ({
40425
40514
  "fast-cycles": typeof prefetchedPercentileCounts.counts["fast-cycles"] === "number" ? prefetchedPercentileCounts.counts["fast-cycles"] : prev["fast-cycles"],
40426
40515
  "slow-cycles": typeof prefetchedPercentileCounts.counts["slow-cycles"] === "number" ? prefetchedPercentileCounts.counts["slow-cycles"] : prev["slow-cycles"]
40427
40516
  }));
40428
- }, [prefetchedPercentileCounts, percentileCountsKey, filterState.percentile]);
40517
+ }, [showPercentileCycleFilters, prefetchedPercentileCounts, percentileCountsKey, filterState.percentile]);
40429
40518
  const fetchPercentileCounts = useCallback(async (options) => {
40519
+ if (!showPercentileCycleFilters) {
40520
+ return;
40521
+ }
40430
40522
  if (!workspaceId || !date || shift === void 0) {
40431
40523
  console.warn("[FileManager] Missing required params for percentile counts fetch");
40432
40524
  return;
@@ -40480,9 +40572,9 @@ var FileManagerFilters = ({
40480
40572
  } catch (error) {
40481
40573
  console.error("[FileManager] Error fetching percentile counts:", error);
40482
40574
  }
40483
- }, [workspaceId, date, shift, filterState.percentile, supabase, percentileCounts, percentileClips, fetchPercentileClips]);
40575
+ }, [workspaceId, date, shift, filterState.percentile, showPercentileCycleFilters, supabase, percentileCounts, percentileClips, fetchPercentileClips]);
40484
40576
  useEffect(() => {
40485
- if (!isReady || !percentileCountsKey) {
40577
+ if (!showPercentileCycleFilters || !isReady || !percentileCountsKey) {
40486
40578
  return;
40487
40579
  }
40488
40580
  const schedule = () => {
@@ -40493,13 +40585,13 @@ var FileManagerFilters = ({
40493
40585
  } else {
40494
40586
  setTimeout(schedule, 0);
40495
40587
  }
40496
- }, [isReady, percentileCountsKey, fetchPercentileCounts]);
40588
+ }, [showPercentileCycleFilters, isReady, percentileCountsKey, fetchPercentileCounts]);
40497
40589
  const shouldShowCategory = useCallback((categoryId) => {
40498
40590
  switch (categoryId) {
40499
40591
  case "fast-cycles":
40500
- return filterState.showFastCycles;
40592
+ return showPercentileCycleFilters && filterState.showFastCycles;
40501
40593
  case "slow-cycles":
40502
- return filterState.showSlowCycles;
40594
+ return showPercentileCycleFilters && filterState.showSlowCycles;
40503
40595
  case "longest-idles":
40504
40596
  return false;
40505
40597
  // filterState.showLongestIdles; // Temporarily disabled
@@ -40512,7 +40604,7 @@ var FileManagerFilters = ({
40512
40604
  default:
40513
40605
  return true;
40514
40606
  }
40515
- }, [filterState]);
40607
+ }, [filterState, showPercentileCycleFilters]);
40516
40608
  const getPercentileIcon = useCallback((type, isExpanded, colorClasses) => {
40517
40609
  const iconMap = {
40518
40610
  "fast-cycles": { icon: TrendingUp, color: "text-green-600" },
@@ -40683,7 +40775,7 @@ var FileManagerFilters = ({
40683
40775
  const filteredSlowCycles = (percentileClips["slow-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""));
40684
40776
  const fastCount = typeof percentileCounts["fast-cycles"] === "number" ? percentileCounts["fast-cycles"] : null;
40685
40777
  const slowCount = typeof percentileCounts["slow-cycles"] === "number" ? percentileCounts["slow-cycles"] : null;
40686
- const percentileCategories = [
40778
+ const percentileCategories = showPercentileCycleFilters ? [
40687
40779
  {
40688
40780
  id: "fast-cycles",
40689
40781
  label: "Fast Cycles",
@@ -40781,7 +40873,7 @@ var FileManagerFilters = ({
40781
40873
  };
40782
40874
  })
40783
40875
  } */
40784
- ];
40876
+ ] : [];
40785
40877
  const orderedIds = ["cycle_completion", "fast-cycles", "slow-cycles", "idle_time"];
40786
40878
  orderedIds.forEach((orderedId) => {
40787
40879
  const percentileCategory = percentileCategories.find((cat) => cat.id === orderedId);
@@ -40806,7 +40898,7 @@ var FileManagerFilters = ({
40806
40898
  }
40807
40899
  });
40808
40900
  return tree;
40809
- }, [categories, expandedNodes, counts, clipMetadata, percentileCounts, percentileClips, shouldShowCategory, getPercentileIcon, isClipInTimeRange, isTimeFilterActive]);
40901
+ }, [categories, expandedNodes, counts, clipMetadata, percentileCounts, percentileClips, shouldShowCategory, getPercentileIcon, isClipInTimeRange, isTimeFilterActive, showPercentileCycleFilters]);
40810
40902
  const toggleExpanded = (nodeId) => {
40811
40903
  const newExpanded = new Set(expandedNodes);
40812
40904
  if (newExpanded.has(nodeId)) {
@@ -40818,10 +40910,10 @@ var FileManagerFilters = ({
40818
40910
  console.log(`[FileManager] Fetching clips for expanded category: ${nodeId}`);
40819
40911
  fetchClipMetadata(nodeId, 1);
40820
40912
  }
40821
- if (nodeId === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
40913
+ if (showPercentileCycleFilters && nodeId === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
40822
40914
  fetchPercentileClips("fast-cycles");
40823
40915
  }
40824
- if (nodeId === "slow-cycles" && (percentileClips["slow-cycles"] || []).length === 0) {
40916
+ if (showPercentileCycleFilters && nodeId === "slow-cycles" && (percentileClips["slow-cycles"] || []).length === 0) {
40825
40917
  fetchPercentileClips("slow-cycles");
40826
40918
  }
40827
40919
  }
@@ -40839,10 +40931,10 @@ var FileManagerFilters = ({
40839
40931
  console.log(`[FileManager] Fetching clips for expanded category: ${node.id}`);
40840
40932
  fetchClipMetadata(node.id, 1);
40841
40933
  }
40842
- if (node.id === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
40934
+ if (showPercentileCycleFilters && node.id === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
40843
40935
  fetchPercentileClips("fast-cycles");
40844
40936
  }
40845
- if (node.id === "slow-cycles" && (percentileClips["slow-cycles"] || []).length === 0) {
40937
+ if (showPercentileCycleFilters && node.id === "slow-cycles" && (percentileClips["slow-cycles"] || []).length === 0) {
40846
40938
  fetchPercentileClips("slow-cycles");
40847
40939
  }
40848
40940
  }
@@ -41280,7 +41372,8 @@ var FileManagerFilters = ({
41280
41372
  };
41281
41373
  var PERCENTILE_PRESETS = [5, 10, 15, 20, 25, 30];
41282
41374
  var AdvancedFilterDialog = ({
41283
- onApply
41375
+ onApply,
41376
+ showPercentileCycleFilters = true
41284
41377
  }) => {
41285
41378
  const {
41286
41379
  state,
@@ -41347,7 +41440,7 @@ var AdvancedFilterDialog = ({
41347
41440
  )
41348
41441
  ] }) }),
41349
41442
  /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-8 overflow-y-auto max-h-[60vh]", children: [
41350
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
41443
+ showPercentileCycleFilters && /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
41351
41444
  /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
41352
41445
  /* @__PURE__ */ jsx("div", { className: "p-2 bg-gradient-to-r from-emerald-100 to-blue-100 rounded-lg", children: /* @__PURE__ */ jsx(Activity, { className: "h-5 w-5 text-emerald-600" }) }),
41353
41446
  /* @__PURE__ */ jsxs("div", { children: [
@@ -41440,7 +41533,7 @@ var AdvancedFilterDialog = ({
41440
41533
  ] })
41441
41534
  ] }),
41442
41535
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
41443
- /* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-br from-green-50 to-red-50 rounded-xl p-4 space-y-3", children: [
41536
+ showPercentileCycleFilters && /* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-br from-green-50 to-red-50 rounded-xl p-4 space-y-3", children: [
41444
41537
  /* @__PURE__ */ jsxs("h4", { className: "text-sm font-bold text-gray-700 uppercase tracking-wide flex items-center space-x-2", children: [
41445
41538
  /* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 text-emerald-600" }),
41446
41539
  /* @__PURE__ */ jsx("span", { children: "Performance Extremes" })
@@ -41721,6 +41814,7 @@ var BottlenecksContent = ({
41721
41814
  const supabase = useSupabase();
41722
41815
  const timezone = useAppTimezone();
41723
41816
  const { isIdleTimeVlmEnabled } = useIdleTimeVlmConfig();
41817
+ const { isFastSlowClipFiltersEnabled } = useCompanyFastSlowClipFiltersEnabled();
41724
41818
  const { shiftConfig, isLoading: isShiftConfigLoading } = useDynamicShiftConfig(lineId);
41725
41819
  const { effectiveShift, effectiveDate } = useMemo(() => {
41726
41820
  if (shift !== void 0 && shift !== null && date) {
@@ -41777,12 +41871,16 @@ var BottlenecksContent = ({
41777
41871
  }), []);
41778
41872
  const [initialFilter, setInitialFilter] = useState("");
41779
41873
  const currentIndexRef = useRef(0);
41874
+ const currentClipIdRef = useRef(null);
41780
41875
  const currentPositionRef = useRef(0);
41781
41876
  const currentTotalRef = useRef(0);
41782
41877
  const activeFilterRef = useRef(initialFilter);
41783
41878
  const isMountedRef = useRef(true);
41784
41879
  const fetchInProgressRef = useRef(/* @__PURE__ */ new Set());
41785
41880
  const refreshCountsTimeoutRef = useRef(null);
41881
+ const awaitingNextClipRef = useRef(false);
41882
+ const retryTimeoutRef = useRef(null);
41883
+ const navigationLockRef = useRef(false);
41786
41884
  const [isPlaying, setIsPlaying] = useState(false);
41787
41885
  const [currentTime, setCurrentTime] = useState(0);
41788
41886
  const [duration, setDuration] = useState(0);
@@ -41791,6 +41889,7 @@ var BottlenecksContent = ({
41791
41889
  const [playbackSpeed, setPlaybackSpeed] = useState(1);
41792
41890
  const [currentPosition, setCurrentPosition] = useState(0);
41793
41891
  const [currentTotal, setCurrentTotal] = useState(0);
41892
+ const [playerInstanceNonce, setPlayerInstanceNonce] = useState(0);
41794
41893
  const [isTransitioning, setIsTransitioning] = useState(false);
41795
41894
  const [pendingVideo, setPendingVideo] = useState(null);
41796
41895
  const [isVideoBuffering, setIsVideoBuffering] = useState(false);
@@ -41847,14 +41946,29 @@ var BottlenecksContent = ({
41847
41946
  const [isFullscreen, setIsFullscreen] = useState(false);
41848
41947
  const categoryMetadataRef = useRef([]);
41849
41948
  const currentMetadataIndexRef = useRef(0);
41949
+ const clearRetryTimeout = useCallback(() => {
41950
+ if (retryTimeoutRef.current) {
41951
+ clearTimeout(retryTimeoutRef.current);
41952
+ retryTimeoutRef.current = null;
41953
+ }
41954
+ }, []);
41955
+ const bumpPlayerInstanceNonce = useCallback(() => {
41956
+ setPlayerInstanceNonce((prev) => prev + 1);
41957
+ }, []);
41850
41958
  const updateActiveFilter = useCallback((newFilter) => {
41851
41959
  console.log(`[BottlenecksContent] Updating active filter: ${activeFilterRef.current} -> ${newFilter}`);
41960
+ awaitingNextClipRef.current = false;
41961
+ navigationLockRef.current = false;
41962
+ clearRetryTimeout();
41852
41963
  setActiveFilter(newFilter);
41853
41964
  activeFilterRef.current = newFilter;
41854
- }, []);
41965
+ }, [clearRetryTimeout]);
41855
41966
  useEffect(() => {
41856
41967
  currentIndexRef.current = currentIndex;
41857
41968
  }, [currentIndex]);
41969
+ useEffect(() => {
41970
+ currentClipIdRef.current = currentClipId;
41971
+ }, [currentClipId]);
41858
41972
  useEffect(() => {
41859
41973
  currentPositionRef.current = currentPosition;
41860
41974
  }, [currentPosition]);
@@ -42263,8 +42377,11 @@ var BottlenecksContent = ({
42263
42377
  }
42264
42378
  }, [activeFilter, s3ClipsService, mergedCounts, loadFirstVideoForCategory, allVideos, firstClip]);
42265
42379
  const isPercentileCategory = useCallback((categoryId) => {
42380
+ if (!isFastSlowClipFiltersEnabled) {
42381
+ return false;
42382
+ }
42266
42383
  return ["fast-cycles", "slow-cycles", "longest-idles"].includes(categoryId);
42267
- }, []);
42384
+ }, [isFastSlowClipFiltersEnabled]);
42268
42385
  const getMetadataCacheKey = useCallback((categoryId) => {
42269
42386
  return `${categoryId}-${effectiveDateString}-${effectiveShiftId}-${snapshotDateTime ?? "nosnap"}-${snapshotClipId ?? "nosnap"}`;
42270
42387
  }, [effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId]);
@@ -42296,6 +42413,21 @@ var BottlenecksContent = ({
42296
42413
  return [categoryId];
42297
42414
  }
42298
42415
  }, []);
42416
+ useEffect(() => {
42417
+ if (isFastSlowClipFiltersEnabled) {
42418
+ return;
42419
+ }
42420
+ if (activeFilter !== "fast-cycles" && activeFilter !== "slow-cycles") {
42421
+ return;
42422
+ }
42423
+ const fallbackFilter = ["cycle_completion", "idle_time"].find((type) => (dynamicCounts[type] || 0) > 0) || clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0)?.type || clipTypes[0]?.type;
42424
+ if (!fallbackFilter || fallbackFilter === activeFilter) {
42425
+ return;
42426
+ }
42427
+ setCategoryMetadata([]);
42428
+ categoryMetadataRef.current = [];
42429
+ updateActiveFilter(fallbackFilter);
42430
+ }, [isFastSlowClipFiltersEnabled, activeFilter, dynamicCounts, clipTypes, updateActiveFilter]);
42299
42431
  useCallback((categoryId) => {
42300
42432
  if (isPercentileCategory(categoryId)) {
42301
42433
  return categoryMetadata.length;
@@ -42325,6 +42457,7 @@ var BottlenecksContent = ({
42325
42457
  }
42326
42458
  }, [isNavigating, currentIndex, filteredVideos.length]);
42327
42459
  const clearLoadingState = useCallback(() => {
42460
+ navigationLockRef.current = false;
42328
42461
  setIsTransitioning(false);
42329
42462
  setIsNavigating(false);
42330
42463
  if (loadingTimeoutRef.current) {
@@ -42336,6 +42469,11 @@ var BottlenecksContent = ({
42336
42469
  if (!workspaceId) {
42337
42470
  return;
42338
42471
  }
42472
+ if (!isFastSlowClipFiltersEnabled && (categoryId === "fast-cycles" || categoryId === "slow-cycles")) {
42473
+ setCategoryMetadata([]);
42474
+ categoryMetadataRef.current = [];
42475
+ return;
42476
+ }
42339
42477
  if (!isEffectiveShiftReady) {
42340
42478
  console.log("[BottlenecksContent] Skipping metadata load - shift/date not ready");
42341
42479
  return;
@@ -42488,7 +42626,7 @@ var BottlenecksContent = ({
42488
42626
  } finally {
42489
42627
  setIsCategoryLoading(false);
42490
42628
  }
42491
- }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, supabase]);
42629
+ }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, supabase]);
42492
42630
  useEffect(() => {
42493
42631
  if (previousFilterRef.current !== activeFilter) {
42494
42632
  console.log(`Filter changed from ${previousFilterRef.current} to ${activeFilter} - resetting to first video`);
@@ -42539,8 +42677,12 @@ var BottlenecksContent = ({
42539
42677
  const loadAndPlayClipById = useCallback(async (clipId, categoryId, position, metadataContext) => {
42540
42678
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
42541
42679
  console.log(`[BottlenecksContent] Loading clip by ID: ${clipId}, category=${categoryId}, position=${position}`);
42680
+ awaitingNextClipRef.current = false;
42681
+ navigationLockRef.current = true;
42682
+ clearRetryTimeout();
42542
42683
  setIsTransitioning(true);
42543
42684
  setIsInitialLoading(true);
42685
+ setIsNavigating(true);
42544
42686
  setError(null);
42545
42687
  if (videoRef.current?.player) {
42546
42688
  try {
@@ -42638,7 +42780,67 @@ var BottlenecksContent = ({
42638
42780
  clearLoadingState();
42639
42781
  }
42640
42782
  }
42641
- }, [workspaceId, s3ClipsService, updateActiveFilter, clearLoadingState, loadCategoryMetadata, applyMetadataSnapshot, mergedCounts, isPercentileCategory]);
42783
+ }, [workspaceId, s3ClipsService, updateActiveFilter, clearLoadingState, clearRetryTimeout, loadCategoryMetadata, applyMetadataSnapshot, mergedCounts, isPercentileCategory]);
42784
+ const restartCurrentClipPlayback = useCallback(() => {
42785
+ if (!currentClipId) {
42786
+ return;
42787
+ }
42788
+ console.log(`[BottlenecksContent] Restarting playback for clip ${currentClipId}`);
42789
+ awaitingNextClipRef.current = false;
42790
+ navigationLockRef.current = true;
42791
+ clearRetryTimeout();
42792
+ setError(null);
42793
+ setIsTransitioning(true);
42794
+ setIsInitialLoading(true);
42795
+ setIsNavigating(true);
42796
+ setIsVideoBuffering(false);
42797
+ bumpPlayerInstanceNonce();
42798
+ }, [currentClipId, clearRetryTimeout, bumpPlayerInstanceNonce]);
42799
+ useEffect(() => {
42800
+ if (!newClipsNotification || !awaitingNextClipRef.current || !s3ClipsService) {
42801
+ return;
42802
+ }
42803
+ const activeCategory = activeFilterRef.current;
42804
+ if (!activeCategory || activeCategory === "all" || isPercentileCategory(activeCategory)) {
42805
+ return;
42806
+ }
42807
+ let cancelled = false;
42808
+ const timer = setTimeout(() => {
42809
+ void (async () => {
42810
+ const incomingClips = [...newClipsNotification.clips].reverse();
42811
+ for (const incomingClip of incomingClips) {
42812
+ if (cancelled || !incomingClip?.id || incomingClip.id === currentClipId) {
42813
+ continue;
42814
+ }
42815
+ const incomingVideo = await s3ClipsService.getClipById(incomingClip.id);
42816
+ if (cancelled || !incomingVideo || incomingVideo.type !== activeCategory) {
42817
+ continue;
42818
+ }
42819
+ console.log(`[BottlenecksContent] Autoplaying incoming ${activeCategory} clip ${incomingClip.id}`);
42820
+ invalidateMetadataCache(activeCategory);
42821
+ await loadAndPlayClipById(incomingClip.id, activeCategory, 1, {
42822
+ clips: [{ clipId: incomingClip.id }],
42823
+ total: Math.max(mergedCounts[activeCategory] || 0, 1)
42824
+ });
42825
+ void loadCategoryMetadata(activeCategory, false, true);
42826
+ return;
42827
+ }
42828
+ })();
42829
+ }, 300);
42830
+ return () => {
42831
+ cancelled = true;
42832
+ clearTimeout(timer);
42833
+ };
42834
+ }, [
42835
+ newClipsNotification,
42836
+ s3ClipsService,
42837
+ isPercentileCategory,
42838
+ currentClipId,
42839
+ invalidateMetadataCache,
42840
+ loadAndPlayClipById,
42841
+ loadCategoryMetadata,
42842
+ mergedCounts
42843
+ ]);
42642
42844
  useCallback(async (categoryId, clipIndex) => {
42643
42845
  console.warn("[BottlenecksContent] loadAndPlayClip is deprecated, use loadAndPlayClipById instead");
42644
42846
  if (!workspaceId || !s3ClipsService || !isMountedRef.current || !isEffectiveShiftReady) return;
@@ -42667,8 +42869,11 @@ var BottlenecksContent = ({
42667
42869
  }
42668
42870
  }, [workspaceId, s3ClipsService, effectiveDateString, effectiveShiftId, loadAndPlayClipById, isEffectiveShiftReady]);
42669
42871
  const handleNext = useCallback(async () => {
42670
- if (!isMountedRef.current) return;
42872
+ if (!isMountedRef.current || navigationLockRef.current) return;
42671
42873
  const currentFilter = activeFilterRef.current;
42874
+ navigationLockRef.current = true;
42875
+ clearRetryTimeout();
42876
+ setIsNavigating(true);
42672
42877
  setIsTransitioning(true);
42673
42878
  setIsInitialLoading(true);
42674
42879
  setError(null);
@@ -42698,10 +42903,32 @@ var BottlenecksContent = ({
42698
42903
  );
42699
42904
  const olderClip = neighbors.previous;
42700
42905
  if (!olderClip) {
42701
- console.log("[handleNext] Already at last clip in category");
42702
- clearLoadingState();
42906
+ let metadataArray3 = categoryMetadataRef.current;
42907
+ if (metadataArray3.length === 0) {
42908
+ console.log(`[handleNext] Metadata empty for ${currentFilter}, loading before loop restart`);
42909
+ await loadCategoryMetadata(currentFilter, false, true);
42910
+ metadataArray3 = categoryMetadataRef.current;
42911
+ }
42912
+ const firstClipMeta = metadataArray3[0];
42913
+ if (firstClipMeta?.clipId && firstClipMeta.clipId !== currentClipId) {
42914
+ console.log(`[handleNext] Reached end of ${currentFilter}, looping back to newest clip ${firstClipMeta.clipId}`);
42915
+ await loadAndPlayClipById(firstClipMeta.clipId, currentFilter, 1, {
42916
+ clips: metadataArray3,
42917
+ total: mergedCounts[currentFilter] || metadataArray3.length
42918
+ });
42919
+ return;
42920
+ }
42921
+ console.log("[handleNext] Reached live edge, waiting for the next clip");
42922
+ awaitingNextClipRef.current = true;
42923
+ navigationLockRef.current = false;
42924
+ setIsInitialLoading(false);
42925
+ setIsVideoBuffering(false);
42926
+ setIsPlaying(false);
42927
+ setIsNavigating(false);
42928
+ setIsTransitioning(false);
42703
42929
  return;
42704
42930
  }
42931
+ awaitingNextClipRef.current = false;
42705
42932
  setPendingVideo(olderClip);
42706
42933
  setCurrentClipId(olderClip.id || null);
42707
42934
  setAllVideos([olderClip]);
@@ -42757,8 +42984,23 @@ var BottlenecksContent = ({
42757
42984
  clearLoadingState();
42758
42985
  }
42759
42986
  } else {
42760
- console.log(`[handleNext] Already at last clip in category`);
42761
- clearLoadingState();
42987
+ const firstClipMeta = metadataArray[0];
42988
+ if (firstClipMeta?.clipId && firstClipMeta.clipId !== currentClipId) {
42989
+ console.log(`[handleNext] Reached end of ${currentFilter}, looping back to newest percentile clip ${firstClipMeta.clipId}`);
42990
+ await loadAndPlayClipById(firstClipMeta.clipId, currentFilter, 1, {
42991
+ clips: metadataArray,
42992
+ total: metadataArray.length
42993
+ });
42994
+ return;
42995
+ }
42996
+ console.log(`[handleNext] Reached live edge for ${currentFilter}, waiting for the next clip`);
42997
+ awaitingNextClipRef.current = true;
42998
+ navigationLockRef.current = false;
42999
+ setIsInitialLoading(false);
43000
+ setIsVideoBuffering(false);
43001
+ setIsPlaying(false);
43002
+ setIsNavigating(false);
43003
+ setIsTransitioning(false);
42762
43004
  }
42763
43005
  } catch (error2) {
42764
43006
  console.error(`[handleNext] Error navigating:`, error2);
@@ -42770,10 +43012,13 @@ var BottlenecksContent = ({
42770
43012
  });
42771
43013
  clearLoadingState();
42772
43014
  }
42773
- }, [clearLoadingState, s3ClipsService, loadCategoryMetadata, isPercentileCategory, workspaceId, currentClipId, effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId, mergedCounts]);
43015
+ }, [clearLoadingState, clearRetryTimeout, s3ClipsService, loadCategoryMetadata, loadAndPlayClipById, isPercentileCategory, workspaceId, currentClipId, effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId, mergedCounts]);
42774
43016
  const handlePrevious = useCallback(async () => {
42775
- if (!isMountedRef.current) return;
43017
+ if (!isMountedRef.current || navigationLockRef.current) return;
42776
43018
  const currentFilter = activeFilterRef.current;
43019
+ navigationLockRef.current = true;
43020
+ clearRetryTimeout();
43021
+ setIsNavigating(true);
42777
43022
  setIsTransitioning(true);
42778
43023
  setIsInitialLoading(true);
42779
43024
  setError(null);
@@ -42871,7 +43116,7 @@ var BottlenecksContent = ({
42871
43116
  });
42872
43117
  clearLoadingState();
42873
43118
  }
42874
- }, [clearLoadingState, s3ClipsService, loadCategoryMetadata, isPercentileCategory, workspaceId, currentClipId, effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId, mergedCounts]);
43119
+ }, [clearLoadingState, clearRetryTimeout, s3ClipsService, loadCategoryMetadata, isPercentileCategory, workspaceId, currentClipId, effectiveDateString, effectiveShiftId, snapshotDateTime, snapshotClipId, mergedCounts]);
42875
43120
  const currentVideo = useMemo(() => {
42876
43121
  if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
42877
43122
  return null;
@@ -43038,6 +43283,8 @@ var BottlenecksContent = ({
43038
43283
  }
43039
43284
  }, [error, playbackSpeed]);
43040
43285
  const handleVideoPlay = useCallback(async (player) => {
43286
+ awaitingNextClipRef.current = false;
43287
+ clearRetryTimeout();
43041
43288
  setIsPlaying(true);
43042
43289
  setIsInitialLoading(false);
43043
43290
  if (currentVideo && !currentVideo.creation_timestamp && s3ClipsService) {
@@ -43059,7 +43306,7 @@ var BottlenecksContent = ({
43059
43306
  console.warn("[BottlenecksContent] Failed to load metadata for current video:", error2);
43060
43307
  }
43061
43308
  }
43062
- }, [currentVideo, s3ClipsService]);
43309
+ }, [currentVideo, s3ClipsService, clearRetryTimeout]);
43063
43310
  const handleVideoPause = useCallback((player) => {
43064
43311
  setIsPlaying(false);
43065
43312
  }, []);
@@ -43070,8 +43317,10 @@ var BottlenecksContent = ({
43070
43317
  setDuration(duration2);
43071
43318
  }, []);
43072
43319
  const handleLoadedData = useCallback((player) => {
43073
- console.log("Video data loaded - NOT clearing loading (wait for playing event)");
43074
- }, []);
43320
+ console.log("Video data loaded - clearing transition overlay");
43321
+ setIsInitialLoading(false);
43322
+ clearLoadingState();
43323
+ }, [clearLoadingState]);
43075
43324
  const handleVideoPlaying = useCallback((player) => {
43076
43325
  console.log("Video playing - hiding transition overlay (most reliable)");
43077
43326
  clearLoadingState();
@@ -43092,6 +43341,7 @@ var BottlenecksContent = ({
43092
43341
  console.error("[BottlenecksContent] Video.js error:", errorInfo);
43093
43342
  setIsPlaying(false);
43094
43343
  setIsVideoBuffering(false);
43344
+ clearRetryTimeout();
43095
43345
  const errorCode = errorInfo?.code || 0;
43096
43346
  const canRetry = errorInfo?.canRetry ?? false;
43097
43347
  const errorMessage = errorInfo?.message || "Unknown error";
@@ -43119,17 +43369,24 @@ var BottlenecksContent = ({
43119
43369
  if (videoRetryCountRef.current < 3 && currentVideo) {
43120
43370
  videoRetryCountRef.current++;
43121
43371
  const retryDelay = 1e3 * videoRetryCountRef.current;
43372
+ const retryClipId = currentVideo.id;
43373
+ const retryCategory = activeFilterRef.current;
43122
43374
  console.log(`[Video Error] Recoverable error - Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
43123
43375
  setError({
43124
43376
  type: "retrying",
43125
43377
  message: `Retrying... (${videoRetryCountRef.current}/3)`,
43126
43378
  isRetrying: true
43127
43379
  });
43128
- setTimeout(() => {
43129
- if (videoRef.current && currentVideo && isMountedRef.current) {
43130
- setError(null);
43131
- videoRef.current.dispose();
43380
+ retryTimeoutRef.current = setTimeout(() => {
43381
+ retryTimeoutRef.current = null;
43382
+ if (!isMountedRef.current) {
43383
+ return;
43132
43384
  }
43385
+ if (currentClipIdRef.current !== retryClipId || activeFilterRef.current !== retryCategory) {
43386
+ console.log("[Video Error] Skipping stale retry for previous clip");
43387
+ return;
43388
+ }
43389
+ restartCurrentClipPlayback();
43133
43390
  }, retryDelay);
43134
43391
  } else {
43135
43392
  console.log("[Video Error] Retries exhausted - showing final error overlay");
@@ -43151,7 +43408,7 @@ var BottlenecksContent = ({
43151
43408
  attempts: 3
43152
43409
  });
43153
43410
  }
43154
- }, [currentVideo, workspaceId, clearLoadingState]);
43411
+ }, [currentVideo, workspaceId, clearLoadingState, clearRetryTimeout, restartCurrentClipPlayback]);
43155
43412
  useEffect(() => {
43156
43413
  isMountedRef.current = true;
43157
43414
  return () => {
@@ -43163,10 +43420,11 @@ var BottlenecksContent = ({
43163
43420
  clearTimeout(loadingTimeoutRef.current);
43164
43421
  loadingTimeoutRef.current = null;
43165
43422
  }
43423
+ clearRetryTimeout();
43166
43424
  setIsCategoryLoading(false);
43167
43425
  setIsNavigating(false);
43168
43426
  };
43169
- }, [s3ClipsService]);
43427
+ }, [s3ClipsService, clearRetryTimeout]);
43170
43428
  useEffect(() => {
43171
43429
  if (filteredVideos.length > 0 && currentIndex < filteredVideos.length) {
43172
43430
  if (error && error.type === "fatal") {
@@ -43382,7 +43640,8 @@ var BottlenecksContent = ({
43382
43640
  isShareLoading,
43383
43641
  isShareCopied,
43384
43642
  options: videoPlayerOptions
43385
- }
43643
+ },
43644
+ `${currentVideo.id}-${playerInstanceNonce}-inline`
43386
43645
  )
43387
43646
  }
43388
43647
  ),
@@ -43413,11 +43672,8 @@ var BottlenecksContent = ({
43413
43672
  "button",
43414
43673
  {
43415
43674
  onClick: () => {
43416
- setError(null);
43417
43675
  videoRetryCountRef.current = 0;
43418
- if (videoRef.current) {
43419
- videoRef.current.dispose();
43420
- }
43676
+ restartCurrentClipPlayback();
43421
43677
  },
43422
43678
  className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
43423
43679
  children: "Retry"
@@ -43649,7 +43905,7 @@ var BottlenecksContent = ({
43649
43905
  currentVideoId: currentVideo?.id,
43650
43906
  counts: mergedCounts,
43651
43907
  isReady: hasInitialLoad,
43652
- prefetchedPercentileCounts: prefetchedPercentileCounts || void 0,
43908
+ prefetchedPercentileCounts: isFastSlowClipFiltersEnabled ? prefetchedPercentileCounts || void 0 : void 0,
43653
43909
  workspaceId,
43654
43910
  date: effectiveDateString,
43655
43911
  shift: effectiveShiftId,
@@ -43658,6 +43914,7 @@ var BottlenecksContent = ({
43658
43914
  targetCycleTime: workspaceTargetCycleTime,
43659
43915
  clipClassifications,
43660
43916
  idleTimeVlmEnabled,
43917
+ showPercentileCycleFilters: isFastSlowClipFiltersEnabled,
43661
43918
  onFilterChange: (filterId) => {
43662
43919
  updateActiveFilter(filterId);
43663
43920
  const category = categoriesToShow.find((cat) => cat.type === filterId);
@@ -43806,7 +44063,8 @@ var BottlenecksContent = ({
43806
44063
  isShareLoading,
43807
44064
  isShareCopied,
43808
44065
  options: videoPlayerOptions
43809
- }
44066
+ },
44067
+ `${currentVideo.id}-${playerInstanceNonce}-fullscreen`
43810
44068
  )
43811
44069
  }
43812
44070
  ),
@@ -43870,6 +44128,7 @@ var BottlenecksContent = ({
43870
44128
  !triageMode && /* @__PURE__ */ jsx(
43871
44129
  AdvancedFilterDialog,
43872
44130
  {
44131
+ showPercentileCycleFilters: isFastSlowClipFiltersEnabled,
43873
44132
  onApply: () => {
43874
44133
  console.log("[BottlenecksContent] Advanced filters applied, will refresh clips...");
43875
44134
  }
@@ -50358,29 +50617,29 @@ var Legend5 = ({
50358
50617
  }) => {
50359
50618
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
50360
50619
  const exclamationLabel = useBottleneckLabel ? "Bottleneck" : "<50% efficiency";
50361
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 sm:gap-3 bg-white/95 rounded-lg shadow-sm px-2 sm:px-4 py-1 sm:py-1.5 border border-gray-200/60 backdrop-blur-sm text-[10px] sm:text-sm", children: [
50620
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 sm:gap-4 text-xs font-medium text-slate-600", children: [
50362
50621
  /* @__PURE__ */ jsxs("div", { className: "font-medium text-gray-700 hidden sm:block", children: [
50363
50622
  metricLabel,
50364
50623
  ":"
50365
50624
  ] }),
50366
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 sm:gap-4", children: [
50367
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
50368
- /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#00AB45]/90 ring-1 ring-[#00AB45]/20" }),
50369
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.green_min, effectiveLegend.green_max) })
50625
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 sm:gap-4", children: [
50626
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
50627
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 sm:w-2.5 sm:h-2.5 rounded-full bg-[#00AB45]" }),
50628
+ /* @__PURE__ */ jsx("span", { children: formatPercentRange(effectiveLegend.green_min, effectiveLegend.green_max) })
50370
50629
  ] }),
50371
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
50372
- /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#FFB020]/90 ring-1 ring-[#FFB020]/20" }),
50373
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.yellow_min, effectiveLegend.yellow_max) })
50630
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
50631
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 sm:w-2.5 sm:h-2.5 rounded-full bg-[#FFB020]" }),
50632
+ /* @__PURE__ */ jsx("span", { children: formatPercentRange(effectiveLegend.yellow_min, effectiveLegend.yellow_max) })
50374
50633
  ] }),
50375
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
50376
- /* @__PURE__ */ jsx("div", { className: "w-1.5 h-1.5 sm:w-2.5 sm:h-2.5 rounded-[12px] bg-[#E34329]/90 ring-1 ring-[#E34329]/20" }),
50377
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: formatPercentRange(effectiveLegend.red_min, effectiveLegend.red_max) })
50634
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
50635
+ /* @__PURE__ */ jsx("div", { className: "w-2 h-2 sm:w-2.5 sm:h-2.5 rounded-full bg-[#E34329]" }),
50636
+ /* @__PURE__ */ jsx("span", { children: formatPercentRange(effectiveLegend.red_min, effectiveLegend.red_max) })
50378
50637
  ] })
50379
50638
  ] }),
50380
- /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-6 bg-gray-200 mx-1" }),
50381
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
50382
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-3 h-3 sm:w-5 sm:h-5 bg-[#E34329]/90 rounded-full ring-1 ring-[#E34329]/20 text-white font-bold text-[8px] sm:text-xs", children: "!" }),
50383
- /* @__PURE__ */ jsx("span", { className: "font-medium text-gray-600", children: exclamationLabel })
50639
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block w-px h-4 bg-slate-200 mx-1" }),
50640
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
50641
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center w-3 h-3 sm:w-4 sm:h-4 bg-[#E34329] rounded-full text-white font-bold text-[8px] sm:text-[10px]", children: "!" }),
50642
+ /* @__PURE__ */ jsx("span", { children: exclamationLabel })
50384
50643
  ] })
50385
50644
  ] });
50386
50645
  };
@@ -50490,6 +50749,7 @@ var WorkspaceGrid = React142__default.memo(({
50490
50749
  isPdfMode = false,
50491
50750
  customWorkspacePositions,
50492
50751
  lineNames = {},
50752
+ lineOrder = [],
50493
50753
  factoryView = "factory",
50494
50754
  line2Uuid = "line-2",
50495
50755
  className = "",
@@ -50500,7 +50760,8 @@ var WorkspaceGrid = React142__default.memo(({
50500
50760
  videoStreamsLoading = false,
50501
50761
  displayNames = {},
50502
50762
  onWorkspaceHover,
50503
- onWorkspaceHoverEnd
50763
+ onWorkspaceHoverEnd,
50764
+ toolbarRightContent
50504
50765
  }) => {
50505
50766
  const dashboardConfig = useDashboardConfig();
50506
50767
  const mapViewEnabled = dashboardConfig?.mapViewConfig?.enabled ?? false;
@@ -50534,29 +50795,30 @@ var WorkspaceGrid = React142__default.memo(({
50534
50795
  () => viewMode === "video" ? getVideoGridLegendLabel(workspaces) : MAP_GRID_LEGEND_LABEL,
50535
50796
  [viewMode, workspaces]
50536
50797
  );
50537
- return /* @__PURE__ */ jsxs("div", { className: `relative w-full h-full overflow-hidden ${className}`, children: [
50538
- /* @__PURE__ */ jsxs("div", { className: "absolute top-0 left-2 sm:left-4 right-2 sm:right-8 z-20", children: [
50539
- /* @__PURE__ */ jsxs("div", { className: "flex flex-row items-center justify-between py-1 sm:py-1.5 gap-2", children: [
50540
- /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsx(Legend5, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) }),
50798
+ return /* @__PURE__ */ jsxs("div", { className: `flex flex-col w-full h-full overflow-hidden bg-slate-50/50 ${className}`, children: [
50799
+ /* @__PURE__ */ jsxs("div", { className: "flex-none px-4 py-3 z-20 flex flex-row items-center justify-between gap-4", children: [
50800
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:block", children: /* @__PURE__ */ jsx("div", { className: "inline-flex bg-white/95 rounded-lg shadow-sm px-4 py-2 border border-slate-200/60 backdrop-blur-sm", children: /* @__PURE__ */ jsx(Legend5, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) }) }),
50801
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-3 shrink-0", children: [
50802
+ toolbarRightContent,
50541
50803
  mapViewEnabled && /* @__PURE__ */ jsx(
50542
50804
  "button",
50543
50805
  {
50544
50806
  onClick: handleViewModeToggle,
50545
- className: "flex items-center gap-2 px-3 py-1.5 bg-white border border-gray-300 rounded-md shadow-sm hover:bg-gray-50 transition-colors duration-200",
50807
+ className: "flex items-center gap-2 px-3 py-1.5 bg-white border border-slate-200 rounded-md shadow-sm hover:bg-slate-50 transition-colors duration-200 text-slate-700",
50546
50808
  title: viewMode === "video" ? "Switch to Map View" : "Switch to Video View",
50547
50809
  children: viewMode === "video" ? /* @__PURE__ */ jsxs(Fragment, { children: [
50548
- /* @__PURE__ */ jsx(Map$1, { className: "w-4 h-4 text-gray-600" }),
50549
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline text-sm text-gray-700", children: "Map View" })
50810
+ /* @__PURE__ */ jsx(Map$1, { className: "w-4 h-4 text-slate-500" }),
50811
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline text-sm font-medium", children: "Map View" })
50550
50812
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
50551
- /* @__PURE__ */ jsx(Video, { className: "w-4 h-4 text-gray-600" }),
50552
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline text-sm text-gray-700", children: "Video View" })
50813
+ /* @__PURE__ */ jsx(Video, { className: "w-4 h-4 text-slate-500" }),
50814
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline text-sm font-medium", children: "Video View" })
50553
50815
  ] })
50554
50816
  }
50555
50817
  )
50556
- ] }),
50557
- /* @__PURE__ */ jsx("div", { className: "sm:hidden mt-1 mr-32", children: /* @__PURE__ */ jsx(Legend5, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) })
50818
+ ] })
50558
50819
  ] }),
50559
- /* @__PURE__ */ jsx("div", { className: "absolute top-14 sm:top-16 left-0 right-0 bottom-0", children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsx(
50820
+ /* @__PURE__ */ jsx("div", { className: "sm:hidden px-3 py-2 bg-white border-b border-slate-200/60 z-10", children: /* @__PURE__ */ jsx(Legend5, { legend, useBottleneckLabel: hasFlowBuffers, metricLabel: legendMetricLabel }) }),
50821
+ /* @__PURE__ */ jsx("div", { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: viewMode === "video" ? /* @__PURE__ */ jsx(
50560
50822
  motion.div,
50561
50823
  {
50562
50824
  initial: { opacity: 0 },
@@ -50568,6 +50830,8 @@ var WorkspaceGrid = React142__default.memo(({
50568
50830
  VideoGridViewComponent,
50569
50831
  {
50570
50832
  workspaces,
50833
+ lineNames,
50834
+ lineOrder,
50571
50835
  videoSources,
50572
50836
  videoStreamsByWorkspaceId,
50573
50837
  videoStreamsLoading,
@@ -59430,6 +59694,7 @@ function HomeView({
59430
59694
  [dbLines]
59431
59695
  );
59432
59696
  const isSupervisor = user?.role_level === "supervisor";
59697
+ const hasUser = Boolean(user);
59433
59698
  const visibleLineIds = useMemo(() => {
59434
59699
  const scoped = Array.from(new Set(allLineIds.filter(Boolean)));
59435
59700
  if (enabledLineIdSet.size === 0) {
@@ -59439,57 +59704,100 @@ function HomeView({
59439
59704
  }, [allLineIds, enabledLineIdSet]);
59440
59705
  const fallbackLineId = visibleLineIds[0] || defaultLineId;
59441
59706
  const defaultHomeLineId = fallbackLineId;
59442
- const availableLineIds = useMemo(() => {
59443
- if (visibleLineIds.length > 1) {
59444
- return [...visibleLineIds, factoryViewId];
59445
- }
59446
- return visibleLineIds;
59447
- }, [visibleLineIds, factoryViewId]);
59448
- const LINE_FILTER_STORAGE_KEY = "optifye_home_line_filter";
59449
- const [selectedLineId, setSelectedLineId] = useState(() => {
59707
+ const visibleLineIdsKey = visibleLineIds.join(",");
59708
+ const normalizeSelectedLineIds = (lineIdsToNormalize) => {
59709
+ const allowedLineIds = new Set(visibleLineIds);
59710
+ const requestedLineIds = new Set((lineIdsToNormalize || []).filter(Boolean));
59711
+ const normalized = visibleLineIds.filter((lineId) => requestedLineIds.has(lineId) && allowedLineIds.has(lineId));
59712
+ if (normalized.length > 0) {
59713
+ return normalized;
59714
+ }
59715
+ if (defaultHomeLineId && allowedLineIds.has(defaultHomeLineId)) {
59716
+ return [defaultHomeLineId];
59717
+ }
59718
+ return visibleLineIds.length > 0 ? [visibleLineIds[0]] : [];
59719
+ };
59720
+ const selectedLineIdsEqual = (left, right) => {
59721
+ if (left.length !== right.length) return false;
59722
+ return left.every((value, index) => value === right[index]);
59723
+ };
59724
+ const isAllLinesSelection = (lineIdsToCheck) => visibleLineIds.length > 0 && lineIdsToCheck.length === visibleLineIds.length;
59725
+ const readPersistedSelectedLineIds = () => {
59450
59726
  if (typeof window === "undefined") {
59451
- return defaultHomeLineId;
59727
+ return null;
59452
59728
  }
59453
59729
  try {
59454
- const savedLineId = sessionStorage.getItem(LINE_FILTER_STORAGE_KEY);
59455
- if (savedLineId) {
59456
- if (availableLineIds.includes(savedLineId)) {
59457
- return savedLineId;
59730
+ const savedLineIds = sessionStorage.getItem("optifye_home_line_filters_v2");
59731
+ if (savedLineIds) {
59732
+ const parsedLineIds = JSON.parse(savedLineIds);
59733
+ if (Array.isArray(parsedLineIds)) {
59734
+ return normalizeSelectedLineIds(parsedLineIds.map((lineId) => String(lineId)));
59735
+ }
59736
+ }
59737
+ const legacyLineId = sessionStorage.getItem("optifye_home_line_filter");
59738
+ if (legacyLineId) {
59739
+ if (legacyLineId === factoryViewId) {
59740
+ return normalizeSelectedLineIds(visibleLineIds);
59458
59741
  }
59742
+ return normalizeSelectedLineIds([legacyLineId]);
59459
59743
  }
59460
59744
  } catch (error) {
59461
59745
  console.warn("Failed to read line filter from sessionStorage:", error);
59462
59746
  }
59463
- return defaultHomeLineId;
59464
- });
59747
+ return null;
59748
+ };
59749
+ const [selectedLineIds, setSelectedLineIds] = useState(() => readPersistedSelectedLineIds() || normalizeSelectedLineIds([defaultHomeLineId]));
59750
+ const [isLineSelectorOpen, setIsLineSelectorOpen] = useState(false);
59751
+ const [pendingSelectedLineIds, setPendingSelectedLineIds] = useState([]);
59752
+ const lineSelectorRef = useRef(null);
59753
+ useEffect(() => {
59754
+ if (isLineSelectorOpen) {
59755
+ setPendingSelectedLineIds(selectedLineIds);
59756
+ }
59757
+ }, [isLineSelectorOpen, selectedLineIds]);
59465
59758
  useEffect(() => {
59466
- if (!user || availableLineIds.length === 0) {
59759
+ if (!hasUser || visibleLineIds.length === 0) {
59467
59760
  return;
59468
59761
  }
59469
- try {
59470
- const savedLineId = sessionStorage.getItem(LINE_FILTER_STORAGE_KEY);
59471
- if (savedLineId && availableLineIds.includes(savedLineId)) {
59472
- if (savedLineId !== selectedLineId) {
59473
- setSelectedLineId(savedLineId);
59474
- }
59475
- return;
59762
+ const restoredLineIds = readPersistedSelectedLineIds();
59763
+ setSelectedLineIds((previousSelectedLineIds) => {
59764
+ if (restoredLineIds) {
59765
+ return selectedLineIdsEqual(restoredLineIds, previousSelectedLineIds) ? previousSelectedLineIds : restoredLineIds;
59476
59766
  }
59767
+ const normalizedCurrentLineIds = normalizeSelectedLineIds(previousSelectedLineIds);
59768
+ return selectedLineIdsEqual(normalizedCurrentLineIds, previousSelectedLineIds) ? previousSelectedLineIds : normalizedCurrentLineIds;
59769
+ });
59770
+ }, [hasUser, visibleLineIdsKey, defaultHomeLineId, factoryViewId]);
59771
+ useEffect(() => {
59772
+ try {
59773
+ sessionStorage.setItem("optifye_home_line_filters_v2", JSON.stringify(selectedLineIds));
59774
+ sessionStorage.removeItem("optifye_home_line_filter");
59477
59775
  } catch (error) {
59478
- console.warn("Failed to read line filter from sessionStorage:", error);
59776
+ console.warn("Failed to save line filter to sessionStorage:", error);
59479
59777
  }
59480
- if (availableLineIds.includes(selectedLineId)) {
59778
+ }, [selectedLineIds]);
59779
+ useEffect(() => {
59780
+ if (!isLineSelectorOpen) {
59481
59781
  return;
59482
59782
  }
59483
- if (defaultHomeLineId !== selectedLineId) {
59484
- setSelectedLineId(defaultHomeLineId);
59485
- }
59486
- }, [
59487
- user,
59488
- availableLineIds,
59489
- defaultHomeLineId,
59490
- selectedLineId,
59491
- LINE_FILTER_STORAGE_KEY
59492
- ]);
59783
+ const handleClickOutside = (event) => {
59784
+ if (lineSelectorRef.current && !lineSelectorRef.current.contains(event.target)) {
59785
+ setIsLineSelectorOpen(false);
59786
+ }
59787
+ };
59788
+ document.addEventListener("mousedown", handleClickOutside);
59789
+ return () => {
59790
+ document.removeEventListener("mousedown", handleClickOutside);
59791
+ };
59792
+ }, [isLineSelectorOpen]);
59793
+ const primarySelectedLineId = selectedLineIds[0] || defaultHomeLineId;
59794
+ const isMultiLineSelection = selectedLineIds.length > 1;
59795
+ const selectedLineIdsKey = selectedLineIds.join(",");
59796
+ const selectedLineIdSet = useMemo(
59797
+ () => new Set(selectedLineIds),
59798
+ [selectedLineIds]
59799
+ );
59800
+ const metricsScopeLineId = isMultiLineSelection ? factoryViewId : primarySelectedLineId;
59493
59801
  const userCompanyId = useMemo(() => {
59494
59802
  return user?.properties?.company_id || user?.company_id || entityConfig.companyId;
59495
59803
  }, [user, entityConfig.companyId]);
@@ -59509,12 +59817,8 @@ function HomeView({
59509
59817
  useEffect(() => {
59510
59818
  const initDisplayNames = async () => {
59511
59819
  try {
59512
- if (selectedLineId === factoryViewId) {
59513
- for (const lineId of visibleLineIds) {
59514
- await preInitializeWorkspaceDisplayNames(lineId);
59515
- }
59516
- } else {
59517
- await preInitializeWorkspaceDisplayNames(selectedLineId);
59820
+ for (const lineId of selectedLineIds) {
59821
+ await preInitializeWorkspaceDisplayNames(lineId);
59518
59822
  }
59519
59823
  setDisplayNamesInitialized(true);
59520
59824
  } catch (error) {
@@ -59523,8 +59827,8 @@ function HomeView({
59523
59827
  }
59524
59828
  };
59525
59829
  initDisplayNames();
59526
- }, [selectedLineId, factoryViewId, visibleLineIds]);
59527
- const displayNameLineId = selectedLineId === factoryViewId ? void 0 : selectedLineId;
59830
+ }, [selectedLineIdsKey]);
59831
+ const displayNameLineId = isMultiLineSelection ? void 0 : primarySelectedLineId;
59528
59832
  const {
59529
59833
  displayNames: workspaceDisplayNames,
59530
59834
  loading: displayNamesLoading,
@@ -59553,7 +59857,8 @@ function HomeView({
59553
59857
  error: metricsError,
59554
59858
  refetch: refetchMetrics
59555
59859
  } = useDashboardMetrics({
59556
- lineId: selectedLineId,
59860
+ lineId: metricsScopeLineId,
59861
+ lineIds: selectedLineIds,
59557
59862
  onLineMetricsUpdate: handleLineMetricsUpdate,
59558
59863
  userAccessibleLineIds: visibleLineIds
59559
59864
  // Pass user's accessible lines for supervisor filtering
@@ -59561,10 +59866,8 @@ function HomeView({
59561
59866
  const trendGroups = useMemo(() => {
59562
59867
  const lineMetricsRows = lineMetrics || [];
59563
59868
  if (!lineMetricsRows.length) return null;
59564
- if (selectedLineId === factoryViewId) {
59565
- const candidateLineIds = visibleLineIds.filter((id3) => id3 && id3 !== factoryViewId);
59566
- if (!candidateLineIds.length) return null;
59567
- const rowsForLines = lineMetricsRows.filter((row2) => candidateLineIds.includes(row2?.line_id));
59869
+ if (selectedLineIds.length > 1) {
59870
+ const rowsForLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
59568
59871
  if (!rowsForLines.length) return null;
59569
59872
  const groupsMap = /* @__PURE__ */ new Map();
59570
59873
  rowsForLines.forEach((row2) => {
@@ -59587,16 +59890,16 @@ function HomeView({
59587
59890
  shiftId: group.shiftId
59588
59891
  }));
59589
59892
  }
59590
- const row = lineMetricsRows.find((r2) => r2?.line_id === selectedLineId);
59893
+ const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
59591
59894
  if (!row?.date || row?.shift_id === void 0 || row?.shift_id === null) {
59592
59895
  return null;
59593
59896
  }
59594
59897
  return [{
59595
- lineIds: [selectedLineId],
59898
+ lineIds: [primarySelectedLineId],
59596
59899
  date: row.date,
59597
59900
  shiftId: row.shift_id
59598
59901
  }];
59599
- }, [selectedLineId, factoryViewId, visibleLineIds, lineMetrics]);
59902
+ }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
59600
59903
  const trendOptions = useMemo(() => {
59601
59904
  if (!trendGroups || !userCompanyId) return null;
59602
59905
  return {
@@ -59624,17 +59927,18 @@ function HomeView({
59624
59927
  }, [workspaceMetrics, metricsLoading, metricsError]);
59625
59928
  const kpis = useMemo(() => {
59626
59929
  const lineMetricsRows = lineMetrics || [];
59627
- if (selectedLineId === factoryViewId) {
59628
- if (metricsLoading && lineMetricsRows.length === 0) return null;
59629
- return aggregateKPIsFromLineMetricsRows(lineMetricsRows);
59930
+ if (selectedLineIds.length > 1) {
59931
+ const rowsForSelectedLines = lineMetricsRows.filter((row2) => selectedLineIdSet.has(row2?.line_id));
59932
+ if (metricsLoading && rowsForSelectedLines.length === 0) return null;
59933
+ return aggregateKPIsFromLineMetricsRows(rowsForSelectedLines);
59630
59934
  }
59631
- const row = lineMetricsRows.find((r2) => r2?.line_id === selectedLineId);
59935
+ const row = lineMetricsRows.find((r2) => r2?.line_id === primarySelectedLineId);
59632
59936
  if (!row) {
59633
59937
  if (metricsLoading) return null;
59634
59938
  return buildKPIsFromLineMetricsRow(null);
59635
59939
  }
59636
59940
  return buildKPIsFromLineMetricsRow(row);
59637
- }, [selectedLineId, factoryViewId, lineMetrics, metricsLoading]);
59941
+ }, [lineMetrics, metricsLoading, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
59638
59942
  const kpisWithTrend = useMemo(() => {
59639
59943
  if (!kpis) return null;
59640
59944
  if (!kpiTrend) return kpis;
@@ -59659,30 +59963,30 @@ function HomeView({
59659
59963
  };
59660
59964
  }, [kpis, kpiTrend]);
59661
59965
  const selectedLineMeta = useMemo(
59662
- () => dbLines.find((line) => line.id === selectedLineId),
59663
- [dbLines, selectedLineId]
59966
+ () => selectedLineIds.length === 1 ? dbLines.find((line) => line.id === primarySelectedLineId) : void 0,
59967
+ [dbLines, primarySelectedLineId, selectedLineIds.length]
59664
59968
  );
59665
- const selectedMonitoringMode = selectedLineId === factoryViewId ? "output" : selectedLineMeta?.monitoring_mode ?? "output";
59969
+ const selectedMonitoringMode = selectedLineIds.length === 1 ? selectedLineMeta?.monitoring_mode ?? "output" : "output";
59666
59970
  const isUptimeMode = selectedMonitoringMode === "uptime";
59667
59971
  const averageIdleTimeSeconds = useMemo(() => {
59668
59972
  if (!isUptimeMode) return null;
59669
- const targetWorkspaces = selectedLineId === factoryViewId ? workspaceMetrics : workspaceMetrics.filter((ws) => ws.line_id === selectedLineId);
59973
+ const targetWorkspaces = workspaceMetrics.filter((ws) => ws.line_id === primarySelectedLineId);
59670
59974
  const idleValues = targetWorkspaces.map((ws) => ws.idle_time).filter((value) => Number.isFinite(value));
59671
59975
  if (idleValues.length === 0) return 0;
59672
59976
  const totalIdle = idleValues.reduce((sum, value) => sum + value, 0);
59673
59977
  return totalIdle / idleValues.length;
59674
- }, [isUptimeMode, selectedLineId, factoryViewId, workspaceMetrics]);
59978
+ }, [isUptimeMode, primarySelectedLineId, workspaceMetrics]);
59675
59979
  const {
59676
59980
  activeBreaks: allActiveBreaks,
59677
59981
  isLoading: breaksLoading,
59678
59982
  error: breaksError
59679
- } = useActiveBreaks(allLineIds);
59983
+ } = useActiveBreaks(visibleLineIds);
59680
59984
  const activeBreaks = useMemo(() => {
59681
- if (selectedLineId === factoryViewId) {
59985
+ if (isAllLinesSelection(selectedLineIds)) {
59682
59986
  return allActiveBreaks;
59683
59987
  }
59684
- return allActiveBreaks.filter((breakItem) => breakItem.lineId === selectedLineId);
59685
- }, [allActiveBreaks, selectedLineId, factoryViewId]);
59988
+ return allActiveBreaks.filter((breakItem) => selectedLineIdSet.has(breakItem.lineId));
59989
+ }, [allActiveBreaks, selectedLineIdSet, selectedLineIds]);
59686
59990
  const activeBreakLineIds = useMemo(
59687
59991
  () => new Set(activeBreaks.map((breakItem) => breakItem.lineId)),
59688
59992
  [activeBreaks]
@@ -59989,7 +60293,7 @@ function HomeView({
59989
60293
  // Round to 1 decimal
59990
60294
  kpisWithTrend?.avgCycleTime?.change,
59991
60295
  kpisWithTrend?.qualityCompliance?.value ? Math.round(kpisWithTrend.qualityCompliance.value) : null,
59992
- selectedLineId
60296
+ selectedLineIdsKey
59993
60297
  ]);
59994
60298
  useEffect(() => {
59995
60299
  setIsHydrated(true);
@@ -60006,27 +60310,62 @@ function HomeView({
60006
60310
  setErrorMessage(null);
60007
60311
  }
60008
60312
  }, [metricsError]);
60009
- const handleLineChange = useCallback((value) => {
60313
+ const getTrackedLineScope = useCallback((lineIdsForScope) => {
60314
+ if (isAllLinesSelection(lineIdsForScope)) {
60315
+ return factoryViewId;
60316
+ }
60317
+ if (lineIdsForScope.length === 1) {
60318
+ return lineIdsForScope[0];
60319
+ }
60320
+ return "custom_multi";
60321
+ }, [factoryViewId, visibleLineIds.length]);
60322
+ const getLineSelectionLabel = useCallback((lineIdsForScope) => {
60323
+ if (isAllLinesSelection(lineIdsForScope)) {
60324
+ return "All Lines";
60325
+ }
60326
+ if (lineIdsForScope.length === 1) {
60327
+ const lineId = lineIdsForScope[0];
60328
+ return mergedLineNames[lineId] || `Line ${lineId.substring(0, 4)}`;
60329
+ }
60330
+ return `${lineIdsForScope.length} Lines Selected`;
60331
+ }, [mergedLineNames, visibleLineIds.length]);
60332
+ const updateSelectedLineIds = useCallback((nextLineIds) => {
60333
+ const normalizedLineIds = normalizeSelectedLineIds(nextLineIds);
60334
+ if (selectedLineIdsEqual(normalizedLineIds, selectedLineIds)) {
60335
+ return;
60336
+ }
60010
60337
  setIsChangingFilter(true);
60011
- setSelectedLineId(value);
60338
+ setSelectedLineIds(normalizedLineIds);
60012
60339
  trackCoreEvent("monitor line filter changed", {
60013
- previous_line_id: selectedLineId,
60014
- new_line_id: value,
60015
- line_name: mergedLineNames[value] || (value === factoryViewId ? "All Lines" : `Line ${value.substring(0, 4)}`)
60340
+ previous_line_id: getTrackedLineScope(selectedLineIds),
60341
+ new_line_id: getTrackedLineScope(normalizedLineIds),
60342
+ previous_line_ids: selectedLineIds,
60343
+ new_line_ids: normalizedLineIds,
60344
+ selected_line_count: normalizedLineIds.length,
60345
+ selection_mode: isAllLinesSelection(normalizedLineIds) ? "all" : normalizedLineIds.length === 1 ? "single" : "custom",
60346
+ line_name: getLineSelectionLabel(normalizedLineIds)
60016
60347
  });
60017
- try {
60018
- sessionStorage.setItem(LINE_FILTER_STORAGE_KEY, value);
60019
- } catch (error) {
60020
- console.warn("Failed to save line filter to sessionStorage:", error);
60348
+ }, [factoryViewId, getLineSelectionLabel, getTrackedLineScope, selectedLineIds, selectedLineIdsKey, visibleLineIds]);
60349
+ useCallback(() => {
60350
+ updateSelectedLineIds(visibleLineIds);
60351
+ }, [updateSelectedLineIds, visibleLineIds]);
60352
+ useCallback((lineId) => {
60353
+ const currentSelection = new Set(selectedLineIds);
60354
+ if (currentSelection.has(lineId)) {
60355
+ if (currentSelection.size <= 1) {
60356
+ return;
60357
+ }
60358
+ currentSelection.delete(lineId);
60359
+ } else {
60360
+ currentSelection.add(lineId);
60021
60361
  }
60022
- }, [LINE_FILTER_STORAGE_KEY, selectedLineId, mergedLineNames, factoryViewId]);
60362
+ updateSelectedLineIds(Array.from(currentSelection));
60363
+ }, [selectedLineIds, updateSelectedLineIds]);
60023
60364
  useEffect(() => {
60024
60365
  if (!metricsLoading && isChangingFilter) {
60025
- if (workspaceMetrics.length > 0 || selectedLineId === factoryViewId) {
60026
- setIsChangingFilter(false);
60027
- }
60366
+ setIsChangingFilter(false);
60028
60367
  }
60029
- }, [metricsLoading, workspaceMetrics, isChangingFilter, selectedLineId, factoryViewId]);
60368
+ }, [metricsLoading, isChangingFilter]);
60030
60369
  useEffect(() => {
60031
60370
  if (!metricsLoading && !hasInitialDataLoaded) {
60032
60371
  setHasInitialDataLoaded(true);
@@ -60045,11 +60384,107 @@ function HomeView({
60045
60384
  if (visibleLineIds.length <= 1) {
60046
60385
  return null;
60047
60386
  }
60048
- return /* @__PURE__ */ jsxs(Select, { onValueChange: handleLineChange, value: selectedLineId, children: [
60049
- /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-9 sm:h-9 text-xs sm:text-sm px-2 sm:px-3", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a line" }) }),
60050
- /* @__PURE__ */ jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md text-xs sm:text-sm", children: availableLineIds.map((id3) => /* @__PURE__ */ jsx(SelectItem, { value: id3, children: mergedLineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
60387
+ const allLinesSelected = isAllLinesSelection(pendingSelectedLineIds);
60388
+ return /* @__PURE__ */ jsxs("div", { ref: lineSelectorRef, className: "relative", children: [
60389
+ /* @__PURE__ */ jsxs(
60390
+ "button",
60391
+ {
60392
+ type: "button",
60393
+ onClick: () => setIsLineSelectorOpen((previous) => !previous),
60394
+ className: "flex min-w-[180px] items-center justify-between gap-2 rounded-md border border-slate-200 bg-white px-3 py-1.5 text-left text-sm font-medium shadow-sm transition-colors hover:bg-slate-50 text-slate-700",
60395
+ "aria-haspopup": "menu",
60396
+ "aria-expanded": isLineSelectorOpen,
60397
+ "aria-label": "Select lines",
60398
+ children: [
60399
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: getLineSelectionLabel(selectedLineIds) }),
60400
+ /* @__PURE__ */ jsx(ChevronDown, { className: `h-4 w-4 text-slate-400 transition-transform ${isLineSelectorOpen ? "rotate-180" : ""}` })
60401
+ ]
60402
+ }
60403
+ ),
60404
+ isLineSelectorOpen ? /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-full z-50 mt-2 w-[280px] rounded-lg border border-slate-200 bg-white p-3 shadow-xl flex flex-col gap-2", children: [
60405
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pb-2 border-b border-slate-100", children: [
60406
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-slate-800", children: "Select Lines" }),
60407
+ /* @__PURE__ */ jsx(
60408
+ "button",
60409
+ {
60410
+ type: "button",
60411
+ onClick: () => setPendingSelectedLineIds([]),
60412
+ className: "text-xs font-medium text-blue-600 hover:text-blue-700 transition-colors",
60413
+ children: "Clear All"
60414
+ }
60415
+ )
60416
+ ] }),
60417
+ /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 text-sm font-medium text-slate-900 hover:bg-slate-50 transition-colors", children: [
60418
+ /* @__PURE__ */ jsx(
60419
+ "input",
60420
+ {
60421
+ type: "checkbox",
60422
+ className: "h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500",
60423
+ checked: allLinesSelected,
60424
+ onChange: () => setPendingSelectedLineIds(allLinesSelected ? [] : visibleLineIds)
60425
+ }
60426
+ ),
60427
+ /* @__PURE__ */ jsx("span", { children: "All Lines" })
60428
+ ] }),
60429
+ /* @__PURE__ */ jsx("div", { className: "max-h-56 space-y-0.5 overflow-y-auto pr-1", children: visibleLineIds.map((lineId) => {
60430
+ const isChecked = pendingSelectedLineIds.includes(lineId);
60431
+ return /* @__PURE__ */ jsxs(
60432
+ "label",
60433
+ {
60434
+ className: "flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 text-sm transition-colors text-slate-700 hover:bg-slate-50",
60435
+ children: [
60436
+ /* @__PURE__ */ jsx(
60437
+ "input",
60438
+ {
60439
+ type: "checkbox",
60440
+ className: "h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500",
60441
+ checked: isChecked,
60442
+ onChange: () => {
60443
+ setPendingSelectedLineIds((prev) => {
60444
+ const current = new Set(prev);
60445
+ if (current.has(lineId)) {
60446
+ current.delete(lineId);
60447
+ } else {
60448
+ current.add(lineId);
60449
+ }
60450
+ return Array.from(current);
60451
+ });
60452
+ }
60453
+ }
60454
+ ),
60455
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: mergedLineNames[lineId] || `Line ${lineId.substring(0, 4)}` })
60456
+ ]
60457
+ },
60458
+ lineId
60459
+ );
60460
+ }) }),
60461
+ /* @__PURE__ */ jsx("div", { className: "pt-3 pb-1 mt-1 border-t border-slate-100 flex justify-end", children: /* @__PURE__ */ jsx(
60462
+ "button",
60463
+ {
60464
+ type: "button",
60465
+ onClick: () => {
60466
+ if (pendingSelectedLineIds.length > 0) {
60467
+ updateSelectedLineIds(pendingSelectedLineIds);
60468
+ }
60469
+ setIsLineSelectorOpen(false);
60470
+ },
60471
+ disabled: pendingSelectedLineIds.length === 0,
60472
+ className: "bg-blue-600 text-white px-4 py-1.5 rounded-md text-sm font-medium hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors w-full",
60473
+ children: "Apply"
60474
+ }
60475
+ ) })
60476
+ ] }) : null
60051
60477
  ] });
60052
- }, [availableLineIds, handleLineChange, selectedLineId, mergedLineNames, factoryViewId, visibleLineIds.length]);
60478
+ }, [
60479
+ getLineSelectionLabel,
60480
+ isLineSelectorOpen,
60481
+ mergedLineNames,
60482
+ selectedLineIds,
60483
+ pendingSelectedLineIds,
60484
+ visibleLineIds,
60485
+ updateSelectedLineIds,
60486
+ isAllLinesSelection
60487
+ ]);
60053
60488
  const useSmoothLoading = (isLoading, minDuration = 400) => {
60054
60489
  const [showLoading, setShowLoading] = useState(isLoading);
60055
60490
  const loadingStartRef2 = useRef(null);
@@ -60095,11 +60530,11 @@ function HomeView({
60095
60530
  const isDataLoading = metricsLoading || displayNamesLoading && workspaceMetrics.length === 0;
60096
60531
  const hasKpiDataReady = useMemo(() => {
60097
60532
  const lineMetricsRows = lineMetrics || [];
60098
- if (selectedLineId === factoryViewId) {
60099
- return lineMetricsRows.length > 0;
60533
+ if (selectedLineIds.length > 1) {
60534
+ return lineMetricsRows.some((row) => selectedLineIdSet.has(row?.line_id));
60100
60535
  }
60101
- return lineMetricsRows.some((row) => row?.line_id === selectedLineId);
60102
- }, [lineMetrics, selectedLineId, factoryViewId]);
60536
+ return lineMetricsRows.some((row) => row?.line_id === primarySelectedLineId);
60537
+ }, [lineMetrics, primarySelectedLineId, selectedLineIdSet, selectedLineIds.length]);
60103
60538
  const isKpiLoading = !hasKpiDataReady;
60104
60539
  useEffect(() => {
60105
60540
  const minLoadingDurationMs = 250;
@@ -60161,76 +60596,77 @@ function HomeView({
60161
60596
  DashboardHeader,
60162
60597
  {
60163
60598
  lineTitle,
60164
- lineId: selectedLineId === factoryViewId ? allLineIds[0] : selectedLineId,
60599
+ lineId: primarySelectedLineId,
60165
60600
  className: "w-full",
60166
60601
  headerControls: kpiSectionControl
60167
60602
  }
60168
60603
  ) }) }),
60169
- /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative", children: [
60170
- /* @__PURE__ */ jsx("div", { className: "absolute right-2 top-1 sm:right-6 sm:top-3 z-30 flex items-center space-x-2", children: lineSelectorComponent && lineSelectorComponent }),
60171
- /* @__PURE__ */ jsx("div", { className: "h-full sm:h-full min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsx(
60172
- motion.div,
60173
- {
60174
- initial: { opacity: 0, scale: 0.98 },
60175
- animate: { opacity: 1, scale: 1 },
60176
- transition: { duration: 0.3 },
60604
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative flex flex-col", children: /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-[calc(100vh-100px)] sm:min-h-0", children: workspaceMetricsWithBreakState.length > 0 ? /* @__PURE__ */ jsx(
60605
+ motion.div,
60606
+ {
60607
+ initial: { opacity: 0, scale: 0.98 },
60608
+ animate: { opacity: 1, scale: 1 },
60609
+ transition: { duration: 0.3 },
60610
+ className: "h-full",
60611
+ children: React142__default.createElement(WorkspaceGrid, {
60612
+ workspaces: workspaceMetricsWithBreakState,
60613
+ lineNames: mergedLineNames,
60614
+ lineOrder: selectedLineIds,
60615
+ factoryView: factoryViewId,
60616
+ legend: efficiencyLegend,
60617
+ videoSources,
60618
+ videoStreamsByWorkspaceId,
60619
+ videoStreamsLoading,
60620
+ displayNames: workspaceDisplayNames,
60621
+ hasFlowBuffers,
60177
60622
  className: "h-full",
60178
- children: React142__default.createElement(WorkspaceGrid, {
60179
- workspaces: workspaceMetricsWithBreakState,
60180
- lineNames,
60181
- factoryView: factoryViewId,
60182
- legend: efficiencyLegend,
60183
- videoSources,
60184
- videoStreamsByWorkspaceId,
60185
- videoStreamsLoading,
60186
- displayNames: workspaceDisplayNames,
60187
- hasFlowBuffers,
60188
- className: "h-full",
60189
- onWorkspaceHover: handleWorkspaceHover,
60190
- onWorkspaceHoverEnd: handleWorkspaceHoverEnd
60191
- })
60192
- },
60193
- selectedLineId
60194
- ) : !shouldShowDataLoading && hasInitialDataLoaded ? /* @__PURE__ */ jsx(
60195
- motion.div,
60196
- {
60197
- initial: { opacity: 0 },
60198
- animate: { opacity: 1 },
60199
- transition: { duration: 0.3 },
60200
- children: /* @__PURE__ */ jsx(NoWorkspaceData, { message: "No workspace data available. Select another line or check configurations." })
60201
- }
60202
- ) : /* @__PURE__ */ jsx(
60203
- motion.div,
60204
- {
60205
- initial: { opacity: 0, scale: 0.98 },
60206
- animate: { opacity: 1, scale: 1 },
60207
- transition: { duration: 0.3 },
60623
+ toolbarRightContent: lineSelectorComponent,
60624
+ onWorkspaceHover: handleWorkspaceHover,
60625
+ onWorkspaceHoverEnd: handleWorkspaceHoverEnd
60626
+ })
60627
+ },
60628
+ selectedLineIdsKey
60629
+ ) : !shouldShowDataLoading && hasInitialDataLoaded ? /* @__PURE__ */ jsx(
60630
+ motion.div,
60631
+ {
60632
+ initial: { opacity: 0 },
60633
+ animate: { opacity: 1 },
60634
+ transition: { duration: 0.3 },
60635
+ children: /* @__PURE__ */ jsx(NoWorkspaceData, { message: "No workspace data available. Adjust the selected lines or check configurations." })
60636
+ }
60637
+ ) : /* @__PURE__ */ jsx(
60638
+ motion.div,
60639
+ {
60640
+ initial: { opacity: 0, scale: 0.98 },
60641
+ animate: { opacity: 1, scale: 1 },
60642
+ transition: { duration: 0.3 },
60643
+ className: "h-full",
60644
+ children: React142__default.createElement(WorkspaceGrid, {
60645
+ workspaces: [],
60646
+ // Show empty grid while loading
60647
+ lineNames: mergedLineNames,
60648
+ lineOrder: selectedLineIds,
60649
+ factoryView: factoryViewId,
60650
+ legend: efficiencyLegend,
60651
+ videoSources,
60652
+ videoStreamsByWorkspaceId,
60653
+ videoStreamsLoading,
60654
+ displayNames: workspaceDisplayNames,
60655
+ hasFlowBuffers,
60208
60656
  className: "h-full",
60209
- children: React142__default.createElement(WorkspaceGrid, {
60210
- workspaces: [],
60211
- // Show empty grid while loading
60212
- lineNames,
60213
- factoryView: factoryViewId,
60214
- legend: efficiencyLegend,
60215
- videoSources,
60216
- videoStreamsByWorkspaceId,
60217
- videoStreamsLoading,
60218
- displayNames: workspaceDisplayNames,
60219
- hasFlowBuffers,
60220
- className: "h-full",
60221
- onWorkspaceHover: handleWorkspaceHover,
60222
- onWorkspaceHoverEnd: handleWorkspaceHoverEnd
60223
- })
60224
- },
60225
- selectedLineId
60226
- ) })
60227
- ] })
60657
+ toolbarRightContent: lineSelectorComponent,
60658
+ onWorkspaceHover: handleWorkspaceHover,
60659
+ onWorkspaceHoverEnd: handleWorkspaceHoverEnd
60660
+ })
60661
+ },
60662
+ selectedLineIdsKey
60663
+ ) }) })
60228
60664
  ] }),
60229
60665
  /* @__PURE__ */ jsx(
60230
60666
  BreakNotificationPopup,
60231
60667
  {
60232
60668
  activeBreaks,
60233
- lineNames,
60669
+ lineNames: mergedLineNames,
60234
60670
  isVisible: !breaksLoading && !breaksError && !breakNotificationsDismissed,
60235
60671
  onDismiss: () => setBreakNotificationsDismissed(true)
60236
60672
  }
@@ -69369,6 +69805,10 @@ var WorkspaceDetailView = ({
69369
69805
  shiftId: selectedShift,
69370
69806
  companyId: dashboardConfig?.entityConfig?.companyId
69371
69807
  });
69808
+ const {
69809
+ isFastSlowClipFiltersEnabled,
69810
+ isResolved: isFastSlowClipFiltersResolved
69811
+ } = useCompanyFastSlowClipFiltersEnabled();
69372
69812
  const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
69373
69813
  dashboardConfig?.supervisorConfig?.enabled || false;
69374
69814
  const effectiveLineId = lineId || selectedLineId;
@@ -69455,15 +69895,19 @@ var WorkspaceDetailView = ({
69455
69895
  return `${workspaceId}:${percentileDate}:${percentileShiftId.toString()}:10`;
69456
69896
  }, [workspaceId, percentileDate, percentileShiftId]);
69457
69897
  useEffect(() => {
69458
- if (!percentileCountsKey) {
69898
+ if (!isFastSlowClipFiltersEnabled || !percentileCountsKey) {
69459
69899
  setPrefetchedPercentileCounts(null);
69460
69900
  return;
69461
69901
  }
69462
69902
  if (prefetchedPercentileCounts && prefetchedPercentileCounts.key !== percentileCountsKey) {
69463
69903
  setPrefetchedPercentileCounts(null);
69464
69904
  }
69465
- }, [percentileCountsKey, prefetchedPercentileCounts]);
69905
+ }, [isFastSlowClipFiltersEnabled, percentileCountsKey, prefetchedPercentileCounts]);
69466
69906
  useEffect(() => {
69907
+ if (!isFastSlowClipFiltersEnabled || !isFastSlowClipFiltersResolved) {
69908
+ setPrefetchedPercentileCounts(null);
69909
+ return;
69910
+ }
69467
69911
  if (!percentileCountsKey || !percentileDate || percentileShiftId === null || percentileShiftId === void 0) {
69468
69912
  return;
69469
69913
  }
@@ -69530,7 +69974,16 @@ var WorkspaceDetailView = ({
69530
69974
  return () => {
69531
69975
  controller.abort();
69532
69976
  };
69533
- }, [percentileCountsKey, percentileDate, percentileShiftId, workspaceId, supabase, prefetchedPercentileCounts]);
69977
+ }, [
69978
+ isFastSlowClipFiltersEnabled,
69979
+ isFastSlowClipFiltersResolved,
69980
+ percentileCountsKey,
69981
+ percentileDate,
69982
+ percentileShiftId,
69983
+ workspaceId,
69984
+ supabase,
69985
+ prefetchedPercentileCounts
69986
+ ]);
69534
69987
  const {
69535
69988
  metrics: historicMetrics,
69536
69989
  isLoading: historicLoading,
@@ -70806,7 +71259,7 @@ var WorkspaceDetailView = ({
70806
71259
  shift,
70807
71260
  totalOutput: workspace?.total_actions,
70808
71261
  workspaceMetrics: detailedWorkspaceMetrics || void 0,
70809
- prefetchedPercentileCounts,
71262
+ prefetchedPercentileCounts: isFastSlowClipFiltersEnabled ? prefetchedPercentileCounts : null,
70810
71263
  className: "h-[calc(100vh-10rem)]"
70811
71264
  }
70812
71265
  ) })
@@ -79770,4 +80223,4 @@ var streamProxyConfig = {
79770
80223
  }
79771
80224
  };
79772
80225
 
79773
- export { ACTION_FAMILIES, ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AvatarUpload, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, ClipsCostView_default as ClipsCostView, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EFFICIENCY_ON_TRACK_THRESHOLD, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, FittingTitle, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, HourlyUptimeChart, ISTTimer_default as ISTTimer, IdleTimeVlmConfigProvider, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend5 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, MobileMenuProvider, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlantHeadView_default as PlantHeadView, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, ROOT_DASHBOARD_EVENT_NAMES, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, SettingsPopup, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UptimeDonutChart, UptimeLineChart, UptimeMetricCards, UserAvatar, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceCycleTimeMetricCards, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, aggregateKPIsFromLineMetricsRows, alertsService, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, awardsService, buildDateKey, buildKPIsFromLineMetricsRow, buildShiftGroupsKey, canRoleAccessDashboardPath, canRoleAccessTeamManagement, canRoleAssignFactories, canRoleAssignLines, canRoleChangeRole, canRoleInviteRole, canRoleManageCompany, canRoleManageTargets, canRoleManageUsers, canRoleRemoveUser, canRoleViewClipsCost, canRoleViewUsageStats, captureSentryException, captureSentryMessage, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearSentryContext, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStorageService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, filterDataByDateKeyRange, forceRefreshWorkspaceDisplayNames, formatAwardMonth, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration2 as formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getActionDisplayName, getActiveShift, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAssignableRoles, getAssignmentColumnLabel, getAvailableShiftIds, getAwardBadgeType, getAwardDescription, getAwardTitle, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getCurrentWeekFullRange, getCurrentWeekToDateRange, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getInitials, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getNextUpdateInterval, getOperationalDate, getRoleAssignmentKind, getRoleDescription, getRoleLabel, getRoleMetadata, getRoleNavPaths, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShiftWorkDurationSeconds, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getVisibleRolesForCurrentUser, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isEfficiencyOnTrack, isFactoryScopedRole, isFullMonthRange, isLegacyConfiguration, isLoopbackHostname, isPrefetchError, isRecentFlowVideoGridMetricMode, isSafari, isSupervisorRole, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWipGatedVideoGridMetricMode, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, lineLeaderboardService, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeActionFamily, normalizeDateKeyRange, normalizeRoleLevel, normalizeVideoGridMetricMode, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, setSentryUserContext, setSentryWorkspaceContext, shouldEnableLocalDevTestLogin, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useClipsInit, useCompanyClipsCost, useCompanyHasVlmEnabledLine, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHideMobileHeader, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useIdleTimeVlmConfig, useKpiTrends, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMobileMenu, useMonthlyTrend, useMultiLineShiftConfigs, useNavigation, useOperationalShiftKey, useOptionalSupabase, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShiftGroups, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthLastSeen, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, useWorkspaceVideoStreams, userService, videoPrefetchManager, videoPreloader, weeklyTopPerformerService, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
80226
+ export { ACTION_FAMILIES, ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AvatarUpload, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, ClipsCostView_default as ClipsCostView, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EFFICIENCY_ON_TRACK_THRESHOLD, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, FittingTitle, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, HourlyUptimeChart, ISTTimer_default as ISTTimer, IdleTimeVlmConfigProvider, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend5 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, MobileMenuProvider, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlantHeadView_default as PlantHeadView, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, ROOT_DASHBOARD_EVENT_NAMES, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, SettingsPopup, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UptimeDonutChart, UptimeLineChart, UptimeMetricCards, UserAvatar, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceCycleTimeMetricCards, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, aggregateKPIsFromLineMetricsRows, alertsService, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, awardsService, buildDateKey, buildKPIsFromLineMetricsRow, buildShiftGroupsKey, canRoleAccessDashboardPath, canRoleAccessTeamManagement, canRoleAssignFactories, canRoleAssignLines, canRoleChangeRole, canRoleInviteRole, canRoleManageCompany, canRoleManageTargets, canRoleManageUsers, canRoleRemoveUser, canRoleViewClipsCost, canRoleViewUsageStats, captureSentryException, captureSentryMessage, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearSentryContext, clearWorkspaceDisplayNamesCache, cn, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStorageService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, fetchIdleTimeReasons, filterDataByDateKeyRange, forceRefreshWorkspaceDisplayNames, formatAwardMonth, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration2 as formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getActionDisplayName, getActiveShift, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAssignableRoles, getAssignmentColumnLabel, getAvailableShiftIds, getAwardBadgeType, getAwardDescription, getAwardTitle, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getCurrentWeekFullRange, getCurrentWeekToDateRange, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getInitials, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getNextUpdateInterval, getOperationalDate, getRoleAssignmentKind, getRoleDescription, getRoleLabel, getRoleMetadata, getRoleNavPaths, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShiftWorkDurationSeconds, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getVisibleRolesForCurrentUser, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isEfficiencyOnTrack, isFactoryScopedRole, isFullMonthRange, isLegacyConfiguration, isLoopbackHostname, isPrefetchError, isRecentFlowVideoGridMetricMode, isSafari, isSupervisorRole, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWipGatedVideoGridMetricMode, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, lineLeaderboardService, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeActionFamily, normalizeDateKeyRange, normalizeRoleLevel, normalizeVideoGridMetricMode, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, setSentryUserContext, setSentryWorkspaceContext, shouldEnableLocalDevTestLogin, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useClipsInit, useCompanyClipsCost, useCompanyFastSlowClipFiltersEnabled, useCompanyHasVlmEnabledLine, useCompanyUsersUsage, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHideMobileHeader, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useIdleTimeVlmConfig, useKpiTrends, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMobileMenu, useMonthlyTrend, useMultiLineShiftConfigs, useNavigation, useOperationalShiftKey, useOptionalSupabase, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShiftGroups, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthLastSeen, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, useWorkspaceVideoStreams, userService, videoPrefetchManager, videoPreloader, weeklyTopPerformerService, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };