@optifye/dashboard-core 6.0.0 → 6.0.2

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
@@ -132,6 +132,9 @@ var DEFAULT_AUTH_CONFIG = {
132
132
  // Defaults related to auth providers, redirects etc.
133
133
  };
134
134
  var DEFAULT_VIDEO_CONFIG = {
135
+ hlsUrls: {
136
+ lineWorkspaceHlsUrls: {}
137
+ },
135
138
  canvasConfig: {
136
139
  fps: 30,
137
140
  useRAF: true
@@ -998,7 +1001,7 @@ var dashboardService = {
998
1001
  const formattedStartDate = formatDate(startDate);
999
1002
  const formattedEndDate = formatDate(endDate);
1000
1003
  try {
1001
- const { data, error } = await supabase.from(metricsTable).select("date, shift_id, efficiency, total_output, avg_cycle_time, ideal_output, avg_pph, pph_threshold, workspace_rank").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1004
+ const { data, error } = await supabase.from(metricsTable).select("date, shift_id, efficiency, total_output, avg_cycle_time, ideal_output, avg_pph, pph_threshold, workspace_rank, idle_time").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1002
1005
  if (error) throw error;
1003
1006
  if (!data) return [];
1004
1007
  const transformedData = data.map((item) => ({
@@ -1011,7 +1014,8 @@ var dashboardService = {
1011
1014
  ideal_output: item.ideal_output || 0,
1012
1015
  avg_pph: item.avg_pph || 0,
1013
1016
  pph_threshold: item.pph_threshold || 0,
1014
- workspace_rank: item.workspace_rank || 0
1017
+ workspace_rank: item.workspace_rank || 0,
1018
+ idle_time: item.idle_time || 0
1015
1019
  }));
1016
1020
  return transformedData;
1017
1021
  } catch (err) {
@@ -5095,19 +5099,23 @@ var useActiveBreaks = (lineIds) => {
5095
5099
  const checkActiveBreaks = useCallback(async () => {
5096
5100
  try {
5097
5101
  setError(null);
5098
- if (!lineIds || lineIds.length === 0) {
5102
+ const validLineIds = lineIds.filter(
5103
+ (id3) => id3 && id3 !== "factory" && id3 !== "all" && // Basic UUID format check
5104
+ id3.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)
5105
+ );
5106
+ if (!validLineIds || validLineIds.length === 0) {
5099
5107
  setActiveBreaks([]);
5100
5108
  setIsLoading(false);
5101
5109
  return;
5102
5110
  }
5103
5111
  const currentMinutes = getCurrentTimeInMinutes();
5104
- const { data: dayShifts, error: dayError } = await supabase.from("line_operating_hours").select("line_id, start_time, end_time, breaks").eq("shift_id", 0).in("line_id", lineIds);
5105
- const { data: nightShifts, error: nightError } = await supabase.from("line_operating_hours").select("line_id, start_time, end_time, breaks").eq("shift_id", 1).in("line_id", lineIds);
5112
+ const { data: dayShifts, error: dayError } = await supabase.from("line_operating_hours").select("line_id, start_time, end_time, breaks").eq("shift_id", 0).in("line_id", validLineIds);
5113
+ const { data: nightShifts, error: nightError } = await supabase.from("line_operating_hours").select("line_id, start_time, end_time, breaks").eq("shift_id", 1).in("line_id", validLineIds);
5106
5114
  if (dayError || nightError) {
5107
5115
  throw new Error("Failed to fetch shift configurations");
5108
5116
  }
5109
5117
  const foundActiveBreaks = [];
5110
- for (const lineId of lineIds) {
5118
+ for (const lineId of validLineIds) {
5111
5119
  const dayShift = dayShifts?.find((s) => s.line_id === lineId);
5112
5120
  const nightShift = nightShifts?.find((s) => s.line_id === lineId);
5113
5121
  if (!dayShift || !nightShift) continue;
@@ -5591,12 +5599,18 @@ var FALLBACK_DISPLAY_NAMES = {
5591
5599
  "WS03": "Filling station"
5592
5600
  // ... Add others if known defaults are useful
5593
5601
  };
5594
- var getConfigurableWorkspaceDisplayName = (workspaceId, workspaceConfig) => {
5602
+ var getConfigurableWorkspaceDisplayName = (workspaceId, workspaceConfig, lineId) => {
5603
+ if (lineId && workspaceConfig?.lineDisplayNames?.[lineId]) {
5604
+ const lineDisplayNames = workspaceConfig.lineDisplayNames[lineId];
5605
+ if (lineDisplayNames[workspaceId]) {
5606
+ return lineDisplayNames[workspaceId];
5607
+ }
5608
+ }
5595
5609
  const displayNames = workspaceConfig?.displayNames || FALLBACK_DISPLAY_NAMES;
5596
5610
  return displayNames[workspaceId] || workspaceId.replace(/^WS/i, "") || workspaceId;
5597
5611
  };
5598
- var getConfigurableShortWorkspaceDisplayName = (workspaceId, workspaceConfig) => {
5599
- const fullName = getConfigurableWorkspaceDisplayName(workspaceId, workspaceConfig);
5612
+ var getConfigurableShortWorkspaceDisplayName = (workspaceId, workspaceConfig, lineId) => {
5613
+ const fullName = getConfigurableWorkspaceDisplayName(workspaceId, workspaceConfig, lineId);
5600
5614
  const match = fullName.match(/([A-Z]\d+(?:-\w+)?)/i);
5601
5615
  if (match && match[0]) {
5602
5616
  return match[0];
@@ -17491,7 +17505,6 @@ var VideoCard = React14__default.memo(({
17491
17505
  });
17492
17506
  }
17493
17507
  const displayName = getWorkspaceDisplayName(workspace.workspace_name);
17494
- workspace.workspace_uuid || workspace.workspace_name;
17495
17508
  const getEfficiencyOverlayColor = (efficiency) => {
17496
17509
  if (efficiency >= 80) {
17497
17510
  return "bg-[#00D654]/25";
@@ -17602,24 +17615,11 @@ var VideoCard = React14__default.memo(({
17602
17615
  return prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.workspace_name === nextProps.workspace.workspace_name && Math.abs(prevProps.workspace.efficiency - nextProps.workspace.efficiency) < 1 && prevProps.hlsUrl === nextProps.hlsUrl && prevProps.shouldPlay === nextProps.shouldPlay && prevProps.cropping?.x === nextProps.cropping?.x && prevProps.cropping?.y === nextProps.cropping?.y && prevProps.cropping?.width === nextProps.cropping?.width && prevProps.cropping?.height === nextProps.cropping?.height;
17603
17616
  });
17604
17617
  VideoCard.displayName = "VideoCard";
17605
- var DEFAULT_WORKSPACE_HLS_URLS = {
17606
- "WS1": "https://dnh-hls.optifye.ai/cam1/index.m3u8",
17607
- "WS2": "https://dnh-hls.optifye.ai/cam2/index.m3u8",
17608
- "WS3": "https://dnh-hls.optifye.ai/cam3/index.m3u8",
17609
- "WS4": "https://dnh-hls.optifye.ai/cam3/index.m3u8",
17610
- "WS01": "https://59.144.218.58:8443/camera6.m3u8",
17611
- "WS02": "https://59.144.218.58:8443/camera2.m3u8",
17612
- "WS03": "https://59.144.218.58:8443/camera3.m3u8",
17613
- "WS04": "https://59.144.218.58:8443/camera4.m3u8",
17614
- "WS05": "https://59.144.218.58:8443/camera1.m3u8",
17615
- "WS06": "https://59.144.218.58:8443/camera5.m3u8"
17616
- };
17617
17618
  var DEFAULT_HLS_URL = "https://192.168.5.9:8443/cam1.m3u8";
17618
17619
  var VideoGridView = React14__default.memo(({
17619
17620
  workspaces,
17620
17621
  selectedLine,
17621
17622
  className = "",
17622
- lineIdMapping = {},
17623
17623
  videoSources = {}
17624
17624
  }) => {
17625
17625
  const router = useRouter();
@@ -17628,13 +17628,20 @@ var VideoGridView = React14__default.memo(({
17628
17628
  const [gridCols, setGridCols] = useState(4);
17629
17629
  const [visibleWorkspaces, setVisibleWorkspaces] = useState(/* @__PURE__ */ new Set());
17630
17630
  const videoConfig = useVideoConfig();
17631
- const { cropping, canvasConfig } = videoConfig;
17631
+ const { cropping, canvasConfig, hlsUrls } = videoConfig;
17632
17632
  const mergedVideoSources = {
17633
- defaultHlsUrl: videoSources.defaultHlsUrl || DEFAULT_HLS_URL,
17634
- workspaceHlsUrls: { ...DEFAULT_WORKSPACE_HLS_URLS, ...videoSources.workspaceHlsUrls }
17633
+ defaultHlsUrl: videoSources.defaultHlsUrl || hlsUrls?.defaultHlsUrl || DEFAULT_HLS_URL,
17634
+ workspaceHlsUrls: { ...videoSources.workspaceHlsUrls, ...hlsUrls?.workspaceHlsUrls },
17635
+ lineWorkspaceHlsUrls: hlsUrls?.lineWorkspaceHlsUrls || {}
17635
17636
  };
17636
- const getWorkspaceHlsUrl = useCallback((workspaceName) => {
17637
+ const getWorkspaceHlsUrl = useCallback((workspaceName, lineId) => {
17637
17638
  const wsName = workspaceName.toUpperCase();
17639
+ if (lineId && mergedVideoSources.lineWorkspaceHlsUrls[lineId]) {
17640
+ const lineUrls = mergedVideoSources.lineWorkspaceHlsUrls[lineId];
17641
+ if (lineUrls[wsName]) {
17642
+ return lineUrls[wsName];
17643
+ }
17644
+ }
17638
17645
  return mergedVideoSources.workspaceHlsUrls[wsName] || mergedVideoSources.defaultHlsUrl;
17639
17646
  }, [mergedVideoSources]);
17640
17647
  const getWorkspaceCropping = useCallback((workspaceName) => {
@@ -17768,16 +17775,17 @@ var VideoGridView = React14__default.memo(({
17768
17775
  minHeight: "100%"
17769
17776
  },
17770
17777
  children: filteredWorkspaces.sort((a, b) => {
17771
- const aNum = parseInt(a.workspace_name.slice(2));
17772
- const bNum = parseInt(b.workspace_name.slice(2));
17773
- if (!selectedLine) {
17774
- const aIsLine2 = a.line_id === lineIdMapping.line2;
17775
- const bIsLine2 = b.line_id === lineIdMapping.line2;
17776
- if (aIsLine2 !== bIsLine2) {
17777
- return aIsLine2 ? 1 : -1;
17778
- }
17778
+ if (a.line_id !== b.line_id) {
17779
+ return (a.line_id || "").localeCompare(b.line_id || "");
17780
+ }
17781
+ const aMatch = a.workspace_name.match(/WS(\d+)/);
17782
+ const bMatch = b.workspace_name.match(/WS(\d+)/);
17783
+ if (aMatch && bMatch) {
17784
+ const aNum = parseInt(aMatch[1]);
17785
+ const bNum = parseInt(bMatch[1]);
17786
+ return aNum - bNum;
17779
17787
  }
17780
- return aNum - bNum;
17788
+ return a.workspace_name.localeCompare(b.workspace_name);
17781
17789
  }).map((workspace) => {
17782
17790
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
17783
17791
  const isVisible = visibleWorkspaces.has(workspaceId);
@@ -17793,7 +17801,7 @@ var VideoGridView = React14__default.memo(({
17793
17801
  VideoCard,
17794
17802
  {
17795
17803
  workspace,
17796
- hlsUrl: getWorkspaceHlsUrl(workspace.workspace_name),
17804
+ hlsUrl: getWorkspaceHlsUrl(workspace.workspace_name, workspace.line_id),
17797
17805
  shouldPlay: isVisible,
17798
17806
  onClick: () => handleWorkspaceClick(workspace),
17799
17807
  onFatalError: throttledReloadDashboard,
@@ -21365,7 +21373,7 @@ function isValidShiftId(shiftId) {
21365
21373
  }
21366
21374
 
21367
21375
  // src/lib/api/s3-clips-parser.ts
21368
- function parseS3Uri(s3Uri) {
21376
+ function parseS3Uri(s3Uri, sopCategories) {
21369
21377
  const path = new URL(s3Uri).pathname;
21370
21378
  const parts = path.split("/").filter((p) => p);
21371
21379
  console.log("S3 URI:", s3Uri);
@@ -21405,6 +21413,27 @@ function parseS3Uri(s3Uri) {
21405
21413
  let description = "Analysis Clip";
21406
21414
  const normalizedViolationType = violationType.toLowerCase().trim();
21407
21415
  console.log(`Parsing violation type: "${violationType}" (normalized: "${normalizedViolationType}") from path: ${s3Uri}`);
21416
+ if (sopCategories && sopCategories.length > 0) {
21417
+ const matchedCategory = sopCategories.find((category) => {
21418
+ const categoryId = category.id.toLowerCase();
21419
+ const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
21420
+ return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
21421
+ normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
21422
+ });
21423
+ if (matchedCategory) {
21424
+ type = matchedCategory.id;
21425
+ description = matchedCategory.description || matchedCategory.label;
21426
+ if (matchedCategory.color.includes("red")) {
21427
+ severity = "high";
21428
+ } else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
21429
+ severity = "medium";
21430
+ } else {
21431
+ severity = "low";
21432
+ }
21433
+ console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
21434
+ return { timestamp, severity, description, type, originalUri: s3Uri };
21435
+ }
21436
+ }
21408
21437
  switch (normalizedViolationType) {
21409
21438
  case "idle_time":
21410
21439
  case "idle":
@@ -21496,6 +21525,11 @@ var S3ClipsService = class {
21496
21525
  if (!config.s3Config) {
21497
21526
  throw new Error("S3 configuration is required");
21498
21527
  }
21528
+ const processing = config.s3Config.processing || {};
21529
+ this.defaultLimitPerCategory = processing.defaultLimitPerCategory || 30;
21530
+ this.maxLimitPerCategory = processing.maxLimitPerCategory || 1e3;
21531
+ this.concurrencyLimit = processing.concurrencyLimit || 10;
21532
+ this.maxInitialFetch = processing.maxInitialFetch || 60;
21499
21533
  const region = this.validateAndSanitizeRegion(config.s3Config.region);
21500
21534
  console.log(`S3ClipsService: Using AWS region: ${region}`);
21501
21535
  this.s3Client = new S3Client({
@@ -21644,11 +21678,23 @@ var S3ClipsService = class {
21644
21678
  const key = url.pathname.startsWith("/") ? url.pathname.substring(1) : url.pathname;
21645
21679
  return `${this.config.s3Config.cloudFrontDomain}/${key}`;
21646
21680
  }
21681
+ /**
21682
+ * Gets SOP categories for a specific workspace
21683
+ */
21684
+ getSOPCategories(workspaceId) {
21685
+ const sopConfig = this.config.s3Config?.sopCategories;
21686
+ if (!sopConfig) return void 0;
21687
+ if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
21688
+ return sopConfig.workspaceOverrides[workspaceId];
21689
+ }
21690
+ return sopConfig.default;
21691
+ }
21647
21692
  /**
21648
21693
  * Processes a single video completely
21649
21694
  */
21650
21695
  async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
21651
- const parsedInfo = parseS3Uri(uri);
21696
+ const sopCategories = this.getSOPCategories(workspaceId);
21697
+ const parsedInfo = parseS3Uri(uri, sopCategories);
21652
21698
  if (!parsedInfo) {
21653
21699
  console.warn(`Skipping URI due to parsing failure: ${uri}`);
21654
21700
  return null;
@@ -21681,27 +21727,32 @@ var S3ClipsService = class {
21681
21727
  async getVideoSummary(workspaceId, date, shiftId) {
21682
21728
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId });
21683
21729
  console.log(`S3ClipsService getVideoSummary: Processing ${s3Uris.length} total URIs for accurate category counts`);
21684
- const counts = {
21685
- best_cycle_time: 0,
21686
- worst_cycle_time: 0,
21687
- bottleneck: 0,
21688
- low_value: 0,
21689
- long_cycle_time: 0,
21690
- missing_quality_check: 0,
21691
- cycle_completions: 0,
21692
- total: 0
21693
- };
21694
- const samples = {
21695
- best_cycle_time: null,
21696
- worst_cycle_time: null,
21697
- bottleneck: null,
21698
- low_value: null,
21699
- long_cycle_time: null,
21700
- missing_quality_check: null,
21701
- cycle_completions: null
21702
- };
21730
+ const sopCategories = this.getSOPCategories(workspaceId);
21731
+ const counts = { total: 0 };
21732
+ const samples = {};
21733
+ if (sopCategories && sopCategories.length > 0) {
21734
+ sopCategories.forEach((category) => {
21735
+ counts[category.id] = 0;
21736
+ samples[category.id] = null;
21737
+ });
21738
+ } else {
21739
+ counts.best_cycle_time = 0;
21740
+ counts.worst_cycle_time = 0;
21741
+ counts.bottleneck = 0;
21742
+ counts.low_value = 0;
21743
+ counts.long_cycle_time = 0;
21744
+ counts.missing_quality_check = 0;
21745
+ counts.cycle_completions = 0;
21746
+ samples.best_cycle_time = null;
21747
+ samples.worst_cycle_time = null;
21748
+ samples.bottleneck = null;
21749
+ samples.low_value = null;
21750
+ samples.long_cycle_time = null;
21751
+ samples.missing_quality_check = null;
21752
+ samples.cycle_completions = null;
21753
+ }
21703
21754
  for (const uri of s3Uris) {
21704
- const parsedInfo = parseS3Uri(uri);
21755
+ const parsedInfo = parseS3Uri(uri, sopCategories);
21705
21756
  if (parsedInfo) {
21706
21757
  const { type } = parsedInfo;
21707
21758
  counts[type] = (counts[type] || 0) + 1;
@@ -21763,9 +21814,9 @@ var S3ClipsService = class {
21763
21814
  }
21764
21815
  return summary;
21765
21816
  }
21766
- const limitPerCategory = limit ? Math.min(Math.max(limit, 1), 1e3) : 30;
21817
+ const limitPerCategory = limit ? Math.min(Math.max(limit, 1), this.maxLimitPerCategory) : this.defaultLimitPerCategory;
21767
21818
  const shouldFetchAll = category === "missing_quality_check" || category === "low_value";
21768
- const initialFetchLimit = shouldFetchAll ? void 0 : category ? limitPerCategory * 3 : void 0;
21819
+ const initialFetchLimit = shouldFetchAll ? void 0 : category ? Math.min(limitPerCategory * 3, this.maxInitialFetch) : void 0;
21769
21820
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId, maxKeys: initialFetchLimit });
21770
21821
  if (s3Uris.length === 0) {
21771
21822
  console.log(`S3ClipsService: No HLS playlists found for workspace ${workspaceId} on date ${date}, shift ${shiftId}`);
@@ -21833,12 +21884,11 @@ var S3ClipsService = class {
21833
21884
  }
21834
21885
  console.log(`S3ClipsService: Total filtered URIs across all categories: ${filteredUris.length}`);
21835
21886
  }
21836
- const concurrencyLimit = 10;
21837
21887
  let processedCount = 0;
21838
21888
  const videoResults = [];
21839
21889
  console.log(`S3ClipsService: Processing ${filteredUris.length} URIs for ${category || "all categories"} with limit ${limitPerCategory} per category`);
21840
- for (let i = 0; i < filteredUris.length; i += concurrencyLimit) {
21841
- const batch = filteredUris.slice(i, i + concurrencyLimit);
21890
+ for (let i = 0; i < filteredUris.length; i += this.concurrencyLimit) {
21891
+ const batch = filteredUris.slice(i, i + this.concurrencyLimit);
21842
21892
  const batchPromises = batch.map(async (uri, batchIndex) => {
21843
21893
  const index = i + batchIndex;
21844
21894
  const result = await this.processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime || false, includeMetadata || (!!timestampStart || !!timestampEnd));
@@ -21914,6 +21964,14 @@ var BottlenecksContent = ({
21914
21964
  className
21915
21965
  }) => {
21916
21966
  const dashboardConfig = useDashboardConfig();
21967
+ const sopCategories = React14__default.useMemo(() => {
21968
+ const sopConfig = dashboardConfig?.s3Config?.sopCategories;
21969
+ if (!sopConfig) return null;
21970
+ if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
21971
+ return sopConfig.workspaceOverrides[workspaceId];
21972
+ }
21973
+ return sopConfig.default;
21974
+ }, [dashboardConfig, workspaceId]);
21917
21975
  const videoRef = useRef(null);
21918
21976
  const fullscreenContainerRef = useRef(null);
21919
21977
  const timestampFilterRef = useRef(null);
@@ -21922,7 +21980,9 @@ var BottlenecksContent = ({
21922
21980
  const [duration, setDuration] = useState(0);
21923
21981
  const [isFullscreen, setIsFullscreen] = useState(false);
21924
21982
  const [currentIndex, setCurrentIndex] = useState(0);
21925
- const [activeFilter, setActiveFilter] = useState("low_value");
21983
+ const [activeFilter, setActiveFilter] = useState(
21984
+ sopCategories && sopCategories.length > 0 ? sopCategories[0].id : "low_value"
21985
+ );
21926
21986
  const [allVideos, setAllVideos] = useState([]);
21927
21987
  const [isLoading, setIsLoading] = useState(true);
21928
21988
  const [error, setError] = useState(null);
@@ -22034,27 +22094,41 @@ var BottlenecksContent = ({
22034
22094
  let filtered = [];
22035
22095
  if (activeFilter === "all") {
22036
22096
  filtered = [...allVideos];
22037
- } else if (activeFilter === "low_value") {
22038
- filtered = allVideos.filter((video) => video.type === "low_value");
22039
- } else if (activeFilter === "sop_deviations") {
22040
- filtered = allVideos.filter((video) => video.type === "missing_quality_check");
22041
- } else if (activeFilter === "best_cycle_time") {
22042
- filtered = allVideos.filter((video) => video.type === "best_cycle_time");
22043
- } else if (activeFilter === "worst_cycle_time") {
22044
- filtered = allVideos.filter((video) => video.type === "worst_cycle_time");
22045
- } else if (activeFilter === "cycle_completions") {
22046
- filtered = allVideos.filter((video) => video.type === "cycle_completions");
22047
- } else if (activeFilter === "long_cycle_time") {
22048
- filtered = allVideos.filter(
22049
- (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22050
- );
22051
22097
  } else {
22052
- filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22098
+ if (sopCategories && sopCategories.length > 0) {
22099
+ const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
22100
+ if (selectedCategory) {
22101
+ filtered = allVideos.filter((video) => video.type === selectedCategory.id);
22102
+ if (selectedCategory.id === "long_cycle_time") {
22103
+ filtered = allVideos.filter(
22104
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22105
+ );
22106
+ }
22107
+ }
22108
+ } else {
22109
+ if (activeFilter === "low_value") {
22110
+ filtered = allVideos.filter((video) => video.type === "low_value");
22111
+ } else if (activeFilter === "sop_deviations") {
22112
+ filtered = allVideos.filter((video) => video.type === "missing_quality_check");
22113
+ } else if (activeFilter === "best_cycle_time") {
22114
+ filtered = allVideos.filter((video) => video.type === "best_cycle_time");
22115
+ } else if (activeFilter === "worst_cycle_time") {
22116
+ filtered = allVideos.filter((video) => video.type === "worst_cycle_time");
22117
+ } else if (activeFilter === "cycle_completions") {
22118
+ filtered = allVideos.filter((video) => video.type === "cycle_completions");
22119
+ } else if (activeFilter === "long_cycle_time") {
22120
+ filtered = allVideos.filter(
22121
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22122
+ );
22123
+ } else {
22124
+ filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22125
+ }
22126
+ }
22053
22127
  }
22054
22128
  return filtered.sort((a, b) => {
22055
22129
  return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
22056
22130
  });
22057
- }, [activeFilter, allVideos]);
22131
+ }, [activeFilter, allVideos, sopCategories]);
22058
22132
  useEffect(() => {
22059
22133
  if (filteredVideos.length === 0) return;
22060
22134
  const upcoming = [];
@@ -22328,35 +22402,34 @@ var BottlenecksContent = ({
22328
22402
  }
22329
22403
  };
22330
22404
  const clipCounts = useMemo(() => {
22331
- if (!allVideos) return {
22332
- bottlenecks: 0,
22333
- lowValue: 0,
22334
- highSeverity: 0,
22335
- mediumSeverity: 0,
22336
- lowSeverity: 0,
22337
- sopDeviations: 0,
22338
- bestCycleTimes: 0,
22339
- worstCycleTimes: 0,
22340
- longCycleTimes: 0,
22341
- cycleCompletions: 0,
22342
- total: 0
22343
- };
22344
- return {
22345
- bottlenecks: allVideos.filter((video) => video.type === "bottleneck").length,
22346
- lowValue: allVideos.filter((video) => video.type === "low_value").length,
22347
- highSeverity: allVideos.filter((video) => video.severity === "high" && video.type === "bottleneck").length,
22348
- mediumSeverity: allVideos.filter((video) => video.severity === "medium" && video.type === "bottleneck").length,
22349
- lowSeverity: allVideos.filter((video) => video.severity === "low" && video.type === "bottleneck").length,
22350
- sopDeviations: allVideos.filter((video) => video.type === "missing_quality_check").length,
22351
- bestCycleTimes: allVideos.filter((video) => video.type === "best_cycle_time").length,
22352
- worstCycleTimes: allVideos.filter((video) => video.type === "worst_cycle_time").length,
22353
- longCycleTimes: allVideos.filter(
22405
+ if (!allVideos) return {};
22406
+ const counts = { total: allVideos.length };
22407
+ if (sopCategories && sopCategories.length > 0) {
22408
+ sopCategories.forEach((category) => {
22409
+ if (category.id === "long_cycle_time") {
22410
+ counts[category.id] = allVideos.filter(
22411
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22412
+ ).length;
22413
+ } else {
22414
+ counts[category.id] = allVideos.filter((video) => video.type === category.id).length;
22415
+ }
22416
+ });
22417
+ } else {
22418
+ counts.bottlenecks = allVideos.filter((video) => video.type === "bottleneck").length;
22419
+ counts.lowValue = allVideos.filter((video) => video.type === "low_value").length;
22420
+ counts.highSeverity = allVideos.filter((video) => video.severity === "high" && video.type === "bottleneck").length;
22421
+ counts.mediumSeverity = allVideos.filter((video) => video.severity === "medium" && video.type === "bottleneck").length;
22422
+ counts.lowSeverity = allVideos.filter((video) => video.severity === "low" && video.type === "bottleneck").length;
22423
+ counts.sopDeviations = allVideos.filter((video) => video.type === "missing_quality_check").length;
22424
+ counts.bestCycleTimes = allVideos.filter((video) => video.type === "best_cycle_time").length;
22425
+ counts.worstCycleTimes = allVideos.filter((video) => video.type === "worst_cycle_time").length;
22426
+ counts.longCycleTimes = allVideos.filter(
22354
22427
  (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22355
- ).length,
22356
- cycleCompletions: allVideos.filter((video) => video.type === "cycle_completions").length,
22357
- total: allVideos.length
22358
- };
22359
- }, [allVideos]);
22428
+ ).length;
22429
+ counts.cycleCompletions = allVideos.filter((video) => video.type === "cycle_completions").length;
22430
+ }
22431
+ return counts;
22432
+ }, [allVideos, sopCategories]);
22360
22433
  const currentVideo = useMemo(() => {
22361
22434
  if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
22362
22435
  return null;
@@ -22378,9 +22451,22 @@ var BottlenecksContent = ({
22378
22451
  return "Cycle Completion";
22379
22452
  case "bottleneck":
22380
22453
  default:
22381
- return "Bottleneck";
22454
+ return "";
22382
22455
  }
22383
22456
  };
22457
+ const getColorClasses = (color2) => {
22458
+ const colorMap = {
22459
+ purple: { text: "text-purple-500", bg: "bg-purple-500", dot: "bg-purple-500" },
22460
+ green: { text: "text-green-600", bg: "bg-green-600", dot: "bg-green-600" },
22461
+ red: { text: "text-red-700", bg: "bg-red-700", dot: "bg-red-700" },
22462
+ "red-dark": { text: "text-red-500", bg: "bg-red-500", dot: "bg-red-500" },
22463
+ blue: { text: "text-blue-600", bg: "bg-blue-600", dot: "bg-blue-600" },
22464
+ orange: { text: "text-orange-600", bg: "bg-orange-600", dot: "bg-orange-600" },
22465
+ yellow: { text: "text-yellow-600", bg: "bg-yellow-600", dot: "bg-yellow-600" },
22466
+ gray: { text: "text-gray-600", bg: "bg-gray-600", dot: "bg-gray-600" }
22467
+ };
22468
+ return colorMap[color2] || colorMap.gray;
22469
+ };
22384
22470
  const formatTimeOnly = (time2) => {
22385
22471
  if (!time2) return "";
22386
22472
  try {
@@ -22410,162 +22496,62 @@ var BottlenecksContent = ({
22410
22496
  /* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error })
22411
22497
  ] });
22412
22498
  }
22499
+ const categoriesToShow = sopCategories && sopCategories.length > 0 ? sopCategories : [
22500
+ // Default hardcoded categories if no configuration
22501
+ { id: "low_value", label: "Idle Moments", color: "purple", subtitle: "Idle time detected" },
22502
+ { id: "best_cycle_time", label: "Best Cycle Time", color: "green", subtitle: "Fastest cycle today" },
22503
+ { id: "worst_cycle_time", label: "Worst Cycle Time", color: "red", subtitle: "Slowest cycle today" },
22504
+ { id: "long_cycle_time", label: "Long Cycle Time", color: "red-dark", subtitle: "Above standard cycle times" },
22505
+ { id: "cycle_completions", label: "Cycle Completions", color: "blue", subtitle: "Completed production cycles" }
22506
+ ];
22413
22507
  return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4 h-[calc(100vh-12rem)]", children: [
22414
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-3 mb-4", children: [
22415
- /* @__PURE__ */ jsxs(
22416
- Card2,
22417
- {
22418
- onClick: () => {
22419
- setActiveFilter("low_value");
22420
- trackCoreEvent("Idle Moments Filter Clicked", {
22421
- workspaceId,
22422
- workspaceName,
22423
- date,
22424
- filterType: "low_value",
22425
- clipCount: clipCounts.lowValue
22426
- });
22427
- },
22428
- className: `bg-white shadow-sm cursor-pointer transition-all duration-200 hover:bg-gray-50 ${activeFilter === "low_value" ? "bg-blue-50 shadow-md ring-1 ring-blue-200" : ""}`,
22429
- "aria-label": `Filter by Idle Moments (${clipCounts.lowValue} clips)`,
22430
- role: "button",
22431
- tabIndex: 0,
22432
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("low_value"),
22433
- children: [
22434
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle2, { className: `text-lg ${activeFilter === "low_value" ? "text-blue-600" : ""}`, children: "Idle Moments" }) }),
22435
- /* @__PURE__ */ jsx(CardContent2, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-center", children: [
22436
- /* @__PURE__ */ jsx("p", { className: "text-3xl font-bold text-purple-500", children: clipCounts.lowValue }),
22437
- /* @__PURE__ */ jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22438
- /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-purple-500 mr-1.5" }),
22439
- /* @__PURE__ */ jsx("span", { children: "Idle time detected" })
22440
- ] })
22441
- ] }) })
22442
- ]
22443
- }
22444
- ),
22445
- /* @__PURE__ */ jsxs(
22446
- Card2,
22447
- {
22448
- onClick: () => {
22449
- setActiveFilter("best_cycle_time");
22450
- trackCoreEvent("Best Cycle Time Filter Clicked", {
22451
- workspaceId,
22452
- workspaceName,
22453
- date,
22454
- filterType: "best_cycle_time",
22455
- clipCount: clipCounts.bestCycleTimes
22456
- });
22457
- },
22458
- className: `bg-white shadow-sm cursor-pointer transition-all duration-200 hover:bg-gray-50 ${activeFilter === "best_cycle_time" ? "bg-blue-50 shadow-md ring-1 ring-blue-200" : ""}`,
22459
- "aria-label": `Filter by Best Cycle Time (${clipCounts.bestCycleTimes} clips)`,
22460
- role: "button",
22461
- tabIndex: 0,
22462
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("best_cycle_time"),
22463
- children: [
22464
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle2, { className: `text-lg ${activeFilter === "best_cycle_time" ? "text-blue-600" : ""}`, children: "Best Cycle Time" }) }),
22465
- /* @__PURE__ */ jsx(CardContent2, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-center", children: [
22466
- /* @__PURE__ */ jsx("p", { className: "text-3xl font-bold text-green-600", children: clipCounts.bestCycleTimes }),
22467
- /* @__PURE__ */ jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22468
- /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-green-600 mr-1.5" }),
22469
- /* @__PURE__ */ jsx("span", { children: "Fastest cycle today" })
22470
- ] })
22471
- ] }) })
22472
- ]
22473
- }
22474
- ),
22475
- /* @__PURE__ */ jsxs(
22476
- Card2,
22477
- {
22478
- onClick: () => {
22479
- setActiveFilter("worst_cycle_time");
22480
- trackCoreEvent("Worst Cycle Time Filter Clicked", {
22481
- workspaceId,
22482
- workspaceName,
22483
- date,
22484
- filterType: "worst_cycle_time",
22485
- clipCount: clipCounts.worstCycleTimes
22486
- });
22487
- },
22488
- className: `bg-white shadow-sm cursor-pointer transition-all duration-200 hover:bg-gray-50 ${activeFilter === "worst_cycle_time" ? "bg-blue-50 shadow-md ring-1 ring-blue-200" : ""}`,
22489
- "aria-label": `Filter by Worst Cycle Time (${clipCounts.worstCycleTimes} clips)`,
22490
- role: "button",
22491
- tabIndex: 0,
22492
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("worst_cycle_time"),
22493
- children: [
22494
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle2, { className: `text-lg ${activeFilter === "worst_cycle_time" ? "text-blue-600" : ""}`, children: "Worst Cycle Time" }) }),
22495
- /* @__PURE__ */ jsx(CardContent2, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-center", children: [
22496
- /* @__PURE__ */ jsx("p", { className: "text-3xl font-bold text-red-700", children: clipCounts.worstCycleTimes }),
22497
- /* @__PURE__ */ jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22498
- /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-red-700 mr-1.5" }),
22499
- /* @__PURE__ */ jsx("span", { children: "Slowest cycle today" })
22500
- ] })
22501
- ] }) })
22502
- ]
22503
- }
22504
- ),
22505
- /* @__PURE__ */ jsxs(
22508
+ /* @__PURE__ */ jsx("div", { className: `grid grid-cols-1 sm:grid-cols-2 ${categoriesToShow.length <= 5 ? "lg:grid-cols-5" : "lg:grid-cols-6"} gap-3 mb-4`, children: categoriesToShow.map((category) => {
22509
+ const colorClasses = getColorClasses(category.color);
22510
+ const count = clipCounts[category.id] || 0;
22511
+ return /* @__PURE__ */ jsxs(
22506
22512
  Card2,
22507
22513
  {
22508
22514
  onClick: () => {
22509
- setActiveFilter("long_cycle_time");
22510
- trackCoreEvent("Long Cycle Time Filter Clicked", {
22515
+ setActiveFilter(category.id);
22516
+ trackCoreEvent(`${category.label} Filter Clicked`, {
22511
22517
  workspaceId,
22512
22518
  workspaceName,
22513
22519
  date,
22514
- filterType: "long_cycle_time",
22515
- clipCount: clipCounts.longCycleTimes
22520
+ filterType: category.id,
22521
+ clipCount: count
22516
22522
  });
22517
22523
  },
22518
- className: `bg-white shadow-sm cursor-pointer transition-all duration-200 hover:bg-gray-50 ${activeFilter === "long_cycle_time" ? "bg-blue-50 shadow-md ring-1 ring-blue-200" : ""}`,
22519
- "aria-label": `Filter by Long Cycle Time Bottlenecks (${clipCounts.longCycleTimes} clips)`,
22524
+ className: `bg-white shadow-sm cursor-pointer transition-all duration-200 hover:bg-gray-50 ${activeFilter === category.id ? "bg-blue-50 shadow-md ring-1 ring-blue-200" : ""}`,
22525
+ "aria-label": `Filter by ${category.label} (${count} clips)`,
22520
22526
  role: "button",
22521
22527
  tabIndex: 0,
22522
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("long_cycle_time"),
22528
+ onKeyDown: (e) => e.key === "Enter" && setActiveFilter(category.id),
22523
22529
  children: [
22524
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle2, { className: `text-lg ${activeFilter === "long_cycle_time" ? "text-blue-600" : ""}`, children: "Long Cycle Time" }) }),
22530
+ /* @__PURE__ */ jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle2, { className: `text-lg ${activeFilter === category.id ? "text-blue-600" : ""}`, children: category.label }) }),
22525
22531
  /* @__PURE__ */ jsx(CardContent2, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-center", children: [
22526
- /* @__PURE__ */ jsx("p", { className: "text-3xl font-bold text-red-500", children: clipCounts.longCycleTimes }),
22532
+ /* @__PURE__ */ jsx("p", { className: `text-3xl font-bold ${colorClasses.text}`, children: count }),
22527
22533
  /* @__PURE__ */ jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22528
- /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-red-500 mr-1.5" }),
22529
- /* @__PURE__ */ jsx("span", { children: "Above standard cycle times" })
22534
+ /* @__PURE__ */ jsx("div", { className: `h-2 w-2 rounded-full ${colorClasses.dot} mr-1.5` }),
22535
+ /* @__PURE__ */ jsx("span", { children: category.subtitle || category.description || category.label })
22530
22536
  ] })
22531
22537
  ] }) })
22532
22538
  ]
22533
- }
22534
- ),
22535
- /* @__PURE__ */ jsxs(
22536
- Card2,
22537
- {
22538
- onClick: () => {
22539
- setActiveFilter("cycle_completions");
22540
- trackCoreEvent("Cycle Completions Filter Clicked", {
22541
- workspaceId,
22542
- workspaceName,
22543
- date,
22544
- filterType: "cycle_completions",
22545
- clipCount: clipCounts.cycleCompletions
22546
- });
22547
- },
22548
- className: `bg-white shadow-sm cursor-pointer transition-all duration-200 hover:bg-gray-50 ${activeFilter === "cycle_completions" ? "bg-blue-50 shadow-md ring-1 ring-blue-200" : ""}`,
22549
- "aria-label": `Filter by Cycle Completions (${clipCounts.cycleCompletions} clips)`,
22550
- role: "button",
22551
- tabIndex: 0,
22552
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("cycle_completions"),
22553
- children: [
22554
- /* @__PURE__ */ jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle2, { className: `text-lg ${activeFilter === "cycle_completions" ? "text-blue-600" : ""}`, children: "Cycle Completions" }) }),
22555
- /* @__PURE__ */ jsx(CardContent2, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col justify-center", children: [
22556
- /* @__PURE__ */ jsx("p", { className: "text-3xl font-bold text-blue-600", children: clipCounts.cycleCompletions }),
22557
- /* @__PURE__ */ jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22558
- /* @__PURE__ */ jsx("div", { className: "h-2 w-2 rounded-full bg-blue-600 mr-1.5" }),
22559
- /* @__PURE__ */ jsx("span", { children: "Completed production cycles" })
22560
- ] })
22561
- ] }) })
22562
- ]
22563
- }
22564
- )
22565
- ] }),
22539
+ },
22540
+ category.id
22541
+ );
22542
+ }) }),
22566
22543
  /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden", style: { height: "calc(100% - 8.5rem)" }, children: [
22567
22544
  /* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-gray-100", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
22568
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-800", children: activeFilter === "low_value" ? `Idle Moments (${clipCounts.lowValue})` : activeFilter === "best_cycle_time" ? `Best Cycle Time (${clipCounts.bestCycleTimes})` : activeFilter === "worst_cycle_time" ? `Worst Cycle Time (${clipCounts.worstCycleTimes})` : activeFilter === "long_cycle_time" ? `Long Cycle Time (${clipCounts.longCycleTimes})` : activeFilter === "cycle_completions" ? `Cycle Completions (${clipCounts.cycleCompletions})` : `All Clips (${clipCounts.total})` }),
22545
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-800", children: (() => {
22546
+ if (activeFilter === "all") {
22547
+ return `All Clips (${clipCounts.total || 0})`;
22548
+ }
22549
+ const activeCategory = categoriesToShow.find((cat) => cat.id === activeFilter);
22550
+ if (activeCategory) {
22551
+ return `${activeCategory.label} (${clipCounts[activeCategory.id] || 0})`;
22552
+ }
22553
+ return activeFilter === "low_value" ? `Idle Moments (${clipCounts.lowValue || 0})` : activeFilter === "best_cycle_time" ? `Best Cycle Time (${clipCounts.bestCycleTimes || 0})` : activeFilter === "worst_cycle_time" ? `Worst Cycle Time (${clipCounts.worstCycleTimes || 0})` : activeFilter === "long_cycle_time" ? `Long Cycle Time (${clipCounts.longCycleTimes || 0})` : activeFilter === "cycle_completions" ? `Cycle Completions (${clipCounts.cycleCompletions || 0})` : `All Clips (${clipCounts.total || 0})`;
22554
+ })() }),
22569
22555
  /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
22570
22556
  /* @__PURE__ */ jsxs("div", { className: "relative", ref: timestampFilterRef, children: [
22571
22557
  /* @__PURE__ */ jsx(
@@ -24941,14 +24927,6 @@ var AIAgentView = () => {
24941
24927
  return newMap;
24942
24928
  });
24943
24929
  },
24944
- onReasoning: (text) => {
24945
- setStreamingStates((prev) => {
24946
- const newMap = new Map(prev);
24947
- const current = newMap.get(currentThreadId) || { message: "", reasoning: "" };
24948
- newMap.set(currentThreadId, { ...current, reasoning: current.reasoning + text });
24949
- return newMap;
24950
- });
24951
- },
24952
24930
  onComplete: async (messageId) => {
24953
24931
  if (currentThreadId && !currentThreadId.startsWith("temp-")) {
24954
24932
  const updatedMessages = await getAllThreadMessages(currentThreadId);
@@ -27337,14 +27315,20 @@ function HomeView({
27337
27315
  }) {
27338
27316
  const [isHydrated, setIsHydrated] = useState(false);
27339
27317
  const availableLineIds = useMemo(() => [factoryViewId, ...allLineIds], [factoryViewId, allLineIds]);
27340
- const [selectedLineId, setSelectedLineId] = useState(defaultLineId);
27318
+ const [selectedLineId, setSelectedLineId] = useState(factoryViewId);
27341
27319
  const [isChangingFilter, setIsChangingFilter] = useState(false);
27342
27320
  const [errorMessage, setErrorMessage] = useState(null);
27343
27321
  const [displayNamesInitialized, setDisplayNamesInitialized] = useState(false);
27344
27322
  useEffect(() => {
27345
27323
  const initDisplayNames = async () => {
27346
27324
  try {
27347
- await preInitializeWorkspaceDisplayNames(selectedLineId);
27325
+ if (selectedLineId === factoryViewId) {
27326
+ for (const lineId of allLineIds) {
27327
+ await preInitializeWorkspaceDisplayNames(lineId);
27328
+ }
27329
+ } else {
27330
+ await preInitializeWorkspaceDisplayNames(selectedLineId);
27331
+ }
27348
27332
  setDisplayNamesInitialized(true);
27349
27333
  } catch (error) {
27350
27334
  console.error("Failed to pre-initialize workspace display names:", error);
@@ -27352,7 +27336,7 @@ function HomeView({
27352
27336
  }
27353
27337
  };
27354
27338
  initDisplayNames();
27355
- }, [selectedLineId]);
27339
+ }, [selectedLineId, factoryViewId, allLineIds]);
27356
27340
  const {
27357
27341
  displayNames: workspaceDisplayNames,
27358
27342
  loading: displayNamesLoading,
@@ -27385,11 +27369,17 @@ function HomeView({
27385
27369
  lineId: selectedLineId,
27386
27370
  onLineMetricsUpdate
27387
27371
  });
27372
+ const lineIdsForBreaks = useMemo(() => {
27373
+ if (selectedLineId === factoryViewId) {
27374
+ return allLineIds;
27375
+ }
27376
+ return [selectedLineId];
27377
+ }, [selectedLineId, factoryViewId, allLineIds]);
27388
27378
  const {
27389
27379
  activeBreaks,
27390
27380
  isLoading: breaksLoading,
27391
27381
  error: breaksError
27392
- } = useActiveBreaks([selectedLineId]);
27382
+ } = useActiveBreaks(lineIdsForBreaks);
27393
27383
  const memoizedWorkspaceMetrics = useMemo(() => workspaceMetrics, [
27394
27384
  // Only update reference if meaningful properties change
27395
27385
  workspaceMetrics.length,
@@ -27432,10 +27422,15 @@ function HomeView({
27432
27422
  const lineTitle = useMemo(() => {
27433
27423
  return factoryName;
27434
27424
  }, [factoryName]);
27435
- const lineSelectorComponent = useMemo(() => /* @__PURE__ */ jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
27436
- /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-9 text-sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a line" }) }),
27437
- /* @__PURE__ */ jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md", children: availableLineIds.map((id3) => /* @__PURE__ */ jsx(SelectItem, { value: id3, children: lineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
27438
- ] }), [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId]);
27425
+ const lineSelectorComponent = useMemo(() => {
27426
+ if (allLineIds.length <= 1) {
27427
+ return null;
27428
+ }
27429
+ return /* @__PURE__ */ jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
27430
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-9 text-sm", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a line" }) }),
27431
+ /* @__PURE__ */ jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md", children: availableLineIds.map((id3) => /* @__PURE__ */ jsx(SelectItem, { value: id3, children: lineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
27432
+ ] });
27433
+ }, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
27439
27434
  const isLoading = !isHydrated || metricsLoading || kpisLoading || isChangingFilter || displayNamesLoading || !displayNamesInitialized;
27440
27435
  if (isLoading) {
27441
27436
  return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-slate-50", children: /* @__PURE__ */ jsx(LoadingPageCmp, { message: "Loading dashboard..." }) });
@@ -27471,7 +27466,7 @@ function HomeView({
27471
27466
  }
27472
27467
  ) }) }),
27473
27468
  /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative", children: [
27474
- /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
27469
+ lineSelectorComponent && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
27475
27470
  memoizedWorkspaceMetrics.length > 0 ? /* @__PURE__ */ jsx("div", { className: "h-full sm:h-full min-h-[calc(100vh-80px)] sm:min-h-0", children: React14__default.createElement(WorkspaceGrid, {
27476
27471
  workspaces: memoizedWorkspaceMetrics,
27477
27472
  lineNames,
@@ -28651,8 +28646,7 @@ var MobileWorkspaceCard = memo(({
28651
28646
  cardClass,
28652
28647
  onWorkspaceClick,
28653
28648
  getMedalIcon,
28654
- line1Id,
28655
- line2Id
28649
+ getLineName
28656
28650
  }) => /* @__PURE__ */ jsxs(
28657
28651
  "div",
28658
28652
  {
@@ -28670,7 +28664,7 @@ var MobileWorkspaceCard = memo(({
28670
28664
  ] }),
28671
28665
  /* @__PURE__ */ jsxs("div", { children: [
28672
28666
  /* @__PURE__ */ jsx("div", { className: "font-semibold text-gray-900", children: getWorkspaceDisplayName(workspace.workspace_name) }),
28673
- /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500", children: workspace.line_id === line1Id ? "Line 1" : workspace.line_id === line2Id ? "Line 2" : workspace.line_id })
28667
+ /* @__PURE__ */ jsx("div", { className: "text-xs text-gray-500", children: getLineName(workspace.line_id) })
28674
28668
  ] })
28675
28669
  ] }),
28676
28670
  /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
@@ -28708,8 +28702,7 @@ var DesktopWorkspaceRow = memo(({
28708
28702
  rowClass,
28709
28703
  onWorkspaceClick,
28710
28704
  getMedalIcon,
28711
- line1Id,
28712
- line2Id
28705
+ getLineName
28713
28706
  }) => /* @__PURE__ */ jsxs(
28714
28707
  "tr",
28715
28708
  {
@@ -28721,7 +28714,7 @@ var DesktopWorkspaceRow = memo(({
28721
28714
  getMedalIcon(index + 1)
28722
28715
  ] }) }),
28723
28716
  /* @__PURE__ */ jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsx("div", { className: "font-medium", children: getWorkspaceDisplayName(workspace.workspace_name) }) }),
28724
- /* @__PURE__ */ jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsx("div", { className: "font-medium", children: workspace.line_id === line1Id ? "Line 1" : workspace.line_id === line2Id ? "Line 2" : workspace.line_id }) }),
28717
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsx("div", { className: "font-medium", children: getLineName(workspace.line_id) }) }),
28725
28718
  /* @__PURE__ */ jsxs("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium whitespace-nowrap", children: [
28726
28719
  (workspace.efficiency || 0).toFixed(1),
28727
28720
  "%"
@@ -28749,10 +28742,19 @@ var LeaderboardDetailView = memo(({
28749
28742
  onWorkspaceClick,
28750
28743
  line1Id = "",
28751
28744
  line2Id = "",
28745
+ lineNames = {},
28752
28746
  className = ""
28753
28747
  }) => {
28754
28748
  const navigation = useNavigation();
28755
28749
  const [sortAscending, setSortAscending] = useState(false);
28750
+ const getLineName = useCallback((lineId2) => {
28751
+ if (lineNames[lineId2]) {
28752
+ return lineNames[lineId2];
28753
+ }
28754
+ if (lineId2 === line1Id) return "Line 1";
28755
+ if (lineId2 === line2Id) return "Line 2";
28756
+ return lineId2;
28757
+ }, [lineNames, line1Id, line2Id]);
28756
28758
  const handleSortToggle = useCallback(() => {
28757
28759
  setSortAscending(!sortAscending);
28758
28760
  }, [sortAscending]);
@@ -28926,8 +28928,7 @@ var LeaderboardDetailView = memo(({
28926
28928
  cardClass,
28927
28929
  onWorkspaceClick: handleWorkspaceClick,
28928
28930
  getMedalIcon,
28929
- line1Id,
28930
- line2Id
28931
+ getLineName
28931
28932
  },
28932
28933
  ws.workspace_uuid
28933
28934
  );
@@ -28952,8 +28953,7 @@ var LeaderboardDetailView = memo(({
28952
28953
  rowClass,
28953
28954
  onWorkspaceClick: handleWorkspaceClick,
28954
28955
  getMedalIcon,
28955
- line1Id,
28956
- line2Id
28956
+ getLineName
28957
28957
  },
28958
28958
  ws.workspace_uuid
28959
28959
  );
@@ -28962,7 +28962,7 @@ var LeaderboardDetailView = memo(({
28962
28962
  ] })
28963
28963
  ] });
28964
28964
  }, (prevProps, nextProps) => {
28965
- return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && prevProps.className === nextProps.className && prevProps.onBackClick === nextProps.onBackClick && prevProps.onWorkspaceClick === nextProps.onWorkspaceClick;
28965
+ return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && JSON.stringify(prevProps.lineNames) === JSON.stringify(nextProps.lineNames) && prevProps.className === nextProps.className && prevProps.onBackClick === nextProps.onBackClick && prevProps.onWorkspaceClick === nextProps.onWorkspaceClick;
28966
28966
  });
28967
28967
  LeaderboardDetailView.displayName = "LeaderboardDetailView";
28968
28968
  var LeaderboardDetailView_default = LeaderboardDetailView;