@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.js CHANGED
@@ -161,6 +161,9 @@ var DEFAULT_AUTH_CONFIG = {
161
161
  // Defaults related to auth providers, redirects etc.
162
162
  };
163
163
  var DEFAULT_VIDEO_CONFIG = {
164
+ hlsUrls: {
165
+ lineWorkspaceHlsUrls: {}
166
+ },
164
167
  canvasConfig: {
165
168
  fps: 30,
166
169
  useRAF: true
@@ -1027,7 +1030,7 @@ var dashboardService = {
1027
1030
  const formattedStartDate = formatDate(startDate);
1028
1031
  const formattedEndDate = formatDate(endDate);
1029
1032
  try {
1030
- 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 });
1033
+ 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 });
1031
1034
  if (error) throw error;
1032
1035
  if (!data) return [];
1033
1036
  const transformedData = data.map((item) => ({
@@ -1040,7 +1043,8 @@ var dashboardService = {
1040
1043
  ideal_output: item.ideal_output || 0,
1041
1044
  avg_pph: item.avg_pph || 0,
1042
1045
  pph_threshold: item.pph_threshold || 0,
1043
- workspace_rank: item.workspace_rank || 0
1046
+ workspace_rank: item.workspace_rank || 0,
1047
+ idle_time: item.idle_time || 0
1044
1048
  }));
1045
1049
  return transformedData;
1046
1050
  } catch (err) {
@@ -5124,19 +5128,23 @@ var useActiveBreaks = (lineIds) => {
5124
5128
  const checkActiveBreaks = React14.useCallback(async () => {
5125
5129
  try {
5126
5130
  setError(null);
5127
- if (!lineIds || lineIds.length === 0) {
5131
+ const validLineIds = lineIds.filter(
5132
+ (id3) => id3 && id3 !== "factory" && id3 !== "all" && // Basic UUID format check
5133
+ id3.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i)
5134
+ );
5135
+ if (!validLineIds || validLineIds.length === 0) {
5128
5136
  setActiveBreaks([]);
5129
5137
  setIsLoading(false);
5130
5138
  return;
5131
5139
  }
5132
5140
  const currentMinutes = getCurrentTimeInMinutes();
5133
- 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);
5134
- 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);
5141
+ 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);
5142
+ 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);
5135
5143
  if (dayError || nightError) {
5136
5144
  throw new Error("Failed to fetch shift configurations");
5137
5145
  }
5138
5146
  const foundActiveBreaks = [];
5139
- for (const lineId of lineIds) {
5147
+ for (const lineId of validLineIds) {
5140
5148
  const dayShift = dayShifts?.find((s) => s.line_id === lineId);
5141
5149
  const nightShift = nightShifts?.find((s) => s.line_id === lineId);
5142
5150
  if (!dayShift || !nightShift) continue;
@@ -5620,12 +5628,18 @@ var FALLBACK_DISPLAY_NAMES = {
5620
5628
  "WS03": "Filling station"
5621
5629
  // ... Add others if known defaults are useful
5622
5630
  };
5623
- var getConfigurableWorkspaceDisplayName = (workspaceId, workspaceConfig) => {
5631
+ var getConfigurableWorkspaceDisplayName = (workspaceId, workspaceConfig, lineId) => {
5632
+ if (lineId && workspaceConfig?.lineDisplayNames?.[lineId]) {
5633
+ const lineDisplayNames = workspaceConfig.lineDisplayNames[lineId];
5634
+ if (lineDisplayNames[workspaceId]) {
5635
+ return lineDisplayNames[workspaceId];
5636
+ }
5637
+ }
5624
5638
  const displayNames = workspaceConfig?.displayNames || FALLBACK_DISPLAY_NAMES;
5625
5639
  return displayNames[workspaceId] || workspaceId.replace(/^WS/i, "") || workspaceId;
5626
5640
  };
5627
- var getConfigurableShortWorkspaceDisplayName = (workspaceId, workspaceConfig) => {
5628
- const fullName = getConfigurableWorkspaceDisplayName(workspaceId, workspaceConfig);
5641
+ var getConfigurableShortWorkspaceDisplayName = (workspaceId, workspaceConfig, lineId) => {
5642
+ const fullName = getConfigurableWorkspaceDisplayName(workspaceId, workspaceConfig, lineId);
5629
5643
  const match = fullName.match(/([A-Z]\d+(?:-\w+)?)/i);
5630
5644
  if (match && match[0]) {
5631
5645
  return match[0];
@@ -17520,7 +17534,6 @@ var VideoCard = React14__namespace.default.memo(({
17520
17534
  });
17521
17535
  }
17522
17536
  const displayName = getWorkspaceDisplayName(workspace.workspace_name);
17523
- workspace.workspace_uuid || workspace.workspace_name;
17524
17537
  const getEfficiencyOverlayColor = (efficiency) => {
17525
17538
  if (efficiency >= 80) {
17526
17539
  return "bg-[#00D654]/25";
@@ -17631,24 +17644,11 @@ var VideoCard = React14__namespace.default.memo(({
17631
17644
  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;
17632
17645
  });
17633
17646
  VideoCard.displayName = "VideoCard";
17634
- var DEFAULT_WORKSPACE_HLS_URLS = {
17635
- "WS1": "https://dnh-hls.optifye.ai/cam1/index.m3u8",
17636
- "WS2": "https://dnh-hls.optifye.ai/cam2/index.m3u8",
17637
- "WS3": "https://dnh-hls.optifye.ai/cam3/index.m3u8",
17638
- "WS4": "https://dnh-hls.optifye.ai/cam3/index.m3u8",
17639
- "WS01": "https://59.144.218.58:8443/camera6.m3u8",
17640
- "WS02": "https://59.144.218.58:8443/camera2.m3u8",
17641
- "WS03": "https://59.144.218.58:8443/camera3.m3u8",
17642
- "WS04": "https://59.144.218.58:8443/camera4.m3u8",
17643
- "WS05": "https://59.144.218.58:8443/camera1.m3u8",
17644
- "WS06": "https://59.144.218.58:8443/camera5.m3u8"
17645
- };
17646
17647
  var DEFAULT_HLS_URL = "https://192.168.5.9:8443/cam1.m3u8";
17647
17648
  var VideoGridView = React14__namespace.default.memo(({
17648
17649
  workspaces,
17649
17650
  selectedLine,
17650
17651
  className = "",
17651
- lineIdMapping = {},
17652
17652
  videoSources = {}
17653
17653
  }) => {
17654
17654
  const router$1 = router.useRouter();
@@ -17657,13 +17657,20 @@ var VideoGridView = React14__namespace.default.memo(({
17657
17657
  const [gridCols, setGridCols] = React14.useState(4);
17658
17658
  const [visibleWorkspaces, setVisibleWorkspaces] = React14.useState(/* @__PURE__ */ new Set());
17659
17659
  const videoConfig = useVideoConfig();
17660
- const { cropping, canvasConfig } = videoConfig;
17660
+ const { cropping, canvasConfig, hlsUrls } = videoConfig;
17661
17661
  const mergedVideoSources = {
17662
- defaultHlsUrl: videoSources.defaultHlsUrl || DEFAULT_HLS_URL,
17663
- workspaceHlsUrls: { ...DEFAULT_WORKSPACE_HLS_URLS, ...videoSources.workspaceHlsUrls }
17662
+ defaultHlsUrl: videoSources.defaultHlsUrl || hlsUrls?.defaultHlsUrl || DEFAULT_HLS_URL,
17663
+ workspaceHlsUrls: { ...videoSources.workspaceHlsUrls, ...hlsUrls?.workspaceHlsUrls },
17664
+ lineWorkspaceHlsUrls: hlsUrls?.lineWorkspaceHlsUrls || {}
17664
17665
  };
17665
- const getWorkspaceHlsUrl = React14.useCallback((workspaceName) => {
17666
+ const getWorkspaceHlsUrl = React14.useCallback((workspaceName, lineId) => {
17666
17667
  const wsName = workspaceName.toUpperCase();
17668
+ if (lineId && mergedVideoSources.lineWorkspaceHlsUrls[lineId]) {
17669
+ const lineUrls = mergedVideoSources.lineWorkspaceHlsUrls[lineId];
17670
+ if (lineUrls[wsName]) {
17671
+ return lineUrls[wsName];
17672
+ }
17673
+ }
17667
17674
  return mergedVideoSources.workspaceHlsUrls[wsName] || mergedVideoSources.defaultHlsUrl;
17668
17675
  }, [mergedVideoSources]);
17669
17676
  const getWorkspaceCropping = React14.useCallback((workspaceName) => {
@@ -17797,16 +17804,17 @@ var VideoGridView = React14__namespace.default.memo(({
17797
17804
  minHeight: "100%"
17798
17805
  },
17799
17806
  children: filteredWorkspaces.sort((a, b) => {
17800
- const aNum = parseInt(a.workspace_name.slice(2));
17801
- const bNum = parseInt(b.workspace_name.slice(2));
17802
- if (!selectedLine) {
17803
- const aIsLine2 = a.line_id === lineIdMapping.line2;
17804
- const bIsLine2 = b.line_id === lineIdMapping.line2;
17805
- if (aIsLine2 !== bIsLine2) {
17806
- return aIsLine2 ? 1 : -1;
17807
- }
17807
+ if (a.line_id !== b.line_id) {
17808
+ return (a.line_id || "").localeCompare(b.line_id || "");
17809
+ }
17810
+ const aMatch = a.workspace_name.match(/WS(\d+)/);
17811
+ const bMatch = b.workspace_name.match(/WS(\d+)/);
17812
+ if (aMatch && bMatch) {
17813
+ const aNum = parseInt(aMatch[1]);
17814
+ const bNum = parseInt(bMatch[1]);
17815
+ return aNum - bNum;
17808
17816
  }
17809
- return aNum - bNum;
17817
+ return a.workspace_name.localeCompare(b.workspace_name);
17810
17818
  }).map((workspace) => {
17811
17819
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
17812
17820
  const isVisible = visibleWorkspaces.has(workspaceId);
@@ -17822,7 +17830,7 @@ var VideoGridView = React14__namespace.default.memo(({
17822
17830
  VideoCard,
17823
17831
  {
17824
17832
  workspace,
17825
- hlsUrl: getWorkspaceHlsUrl(workspace.workspace_name),
17833
+ hlsUrl: getWorkspaceHlsUrl(workspace.workspace_name, workspace.line_id),
17826
17834
  shouldPlay: isVisible,
17827
17835
  onClick: () => handleWorkspaceClick(workspace),
17828
17836
  onFatalError: throttledReloadDashboard,
@@ -21394,7 +21402,7 @@ function isValidShiftId(shiftId) {
21394
21402
  }
21395
21403
 
21396
21404
  // src/lib/api/s3-clips-parser.ts
21397
- function parseS3Uri(s3Uri) {
21405
+ function parseS3Uri(s3Uri, sopCategories) {
21398
21406
  const path = new URL(s3Uri).pathname;
21399
21407
  const parts = path.split("/").filter((p) => p);
21400
21408
  console.log("S3 URI:", s3Uri);
@@ -21434,6 +21442,27 @@ function parseS3Uri(s3Uri) {
21434
21442
  let description = "Analysis Clip";
21435
21443
  const normalizedViolationType = violationType.toLowerCase().trim();
21436
21444
  console.log(`Parsing violation type: "${violationType}" (normalized: "${normalizedViolationType}") from path: ${s3Uri}`);
21445
+ if (sopCategories && sopCategories.length > 0) {
21446
+ const matchedCategory = sopCategories.find((category) => {
21447
+ const categoryId = category.id.toLowerCase();
21448
+ const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
21449
+ return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
21450
+ normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
21451
+ });
21452
+ if (matchedCategory) {
21453
+ type = matchedCategory.id;
21454
+ description = matchedCategory.description || matchedCategory.label;
21455
+ if (matchedCategory.color.includes("red")) {
21456
+ severity = "high";
21457
+ } else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
21458
+ severity = "medium";
21459
+ } else {
21460
+ severity = "low";
21461
+ }
21462
+ console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
21463
+ return { timestamp, severity, description, type, originalUri: s3Uri };
21464
+ }
21465
+ }
21437
21466
  switch (normalizedViolationType) {
21438
21467
  case "idle_time":
21439
21468
  case "idle":
@@ -21525,6 +21554,11 @@ var S3ClipsService = class {
21525
21554
  if (!config.s3Config) {
21526
21555
  throw new Error("S3 configuration is required");
21527
21556
  }
21557
+ const processing = config.s3Config.processing || {};
21558
+ this.defaultLimitPerCategory = processing.defaultLimitPerCategory || 30;
21559
+ this.maxLimitPerCategory = processing.maxLimitPerCategory || 1e3;
21560
+ this.concurrencyLimit = processing.concurrencyLimit || 10;
21561
+ this.maxInitialFetch = processing.maxInitialFetch || 60;
21528
21562
  const region = this.validateAndSanitizeRegion(config.s3Config.region);
21529
21563
  console.log(`S3ClipsService: Using AWS region: ${region}`);
21530
21564
  this.s3Client = new clientS3.S3Client({
@@ -21673,11 +21707,23 @@ var S3ClipsService = class {
21673
21707
  const key = url.pathname.startsWith("/") ? url.pathname.substring(1) : url.pathname;
21674
21708
  return `${this.config.s3Config.cloudFrontDomain}/${key}`;
21675
21709
  }
21710
+ /**
21711
+ * Gets SOP categories for a specific workspace
21712
+ */
21713
+ getSOPCategories(workspaceId) {
21714
+ const sopConfig = this.config.s3Config?.sopCategories;
21715
+ if (!sopConfig) return void 0;
21716
+ if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
21717
+ return sopConfig.workspaceOverrides[workspaceId];
21718
+ }
21719
+ return sopConfig.default;
21720
+ }
21676
21721
  /**
21677
21722
  * Processes a single video completely
21678
21723
  */
21679
21724
  async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
21680
- const parsedInfo = parseS3Uri(uri);
21725
+ const sopCategories = this.getSOPCategories(workspaceId);
21726
+ const parsedInfo = parseS3Uri(uri, sopCategories);
21681
21727
  if (!parsedInfo) {
21682
21728
  console.warn(`Skipping URI due to parsing failure: ${uri}`);
21683
21729
  return null;
@@ -21710,27 +21756,32 @@ var S3ClipsService = class {
21710
21756
  async getVideoSummary(workspaceId, date, shiftId) {
21711
21757
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId });
21712
21758
  console.log(`S3ClipsService getVideoSummary: Processing ${s3Uris.length} total URIs for accurate category counts`);
21713
- const counts = {
21714
- best_cycle_time: 0,
21715
- worst_cycle_time: 0,
21716
- bottleneck: 0,
21717
- low_value: 0,
21718
- long_cycle_time: 0,
21719
- missing_quality_check: 0,
21720
- cycle_completions: 0,
21721
- total: 0
21722
- };
21723
- const samples = {
21724
- best_cycle_time: null,
21725
- worst_cycle_time: null,
21726
- bottleneck: null,
21727
- low_value: null,
21728
- long_cycle_time: null,
21729
- missing_quality_check: null,
21730
- cycle_completions: null
21731
- };
21759
+ const sopCategories = this.getSOPCategories(workspaceId);
21760
+ const counts = { total: 0 };
21761
+ const samples = {};
21762
+ if (sopCategories && sopCategories.length > 0) {
21763
+ sopCategories.forEach((category) => {
21764
+ counts[category.id] = 0;
21765
+ samples[category.id] = null;
21766
+ });
21767
+ } else {
21768
+ counts.best_cycle_time = 0;
21769
+ counts.worst_cycle_time = 0;
21770
+ counts.bottleneck = 0;
21771
+ counts.low_value = 0;
21772
+ counts.long_cycle_time = 0;
21773
+ counts.missing_quality_check = 0;
21774
+ counts.cycle_completions = 0;
21775
+ samples.best_cycle_time = null;
21776
+ samples.worst_cycle_time = null;
21777
+ samples.bottleneck = null;
21778
+ samples.low_value = null;
21779
+ samples.long_cycle_time = null;
21780
+ samples.missing_quality_check = null;
21781
+ samples.cycle_completions = null;
21782
+ }
21732
21783
  for (const uri of s3Uris) {
21733
- const parsedInfo = parseS3Uri(uri);
21784
+ const parsedInfo = parseS3Uri(uri, sopCategories);
21734
21785
  if (parsedInfo) {
21735
21786
  const { type } = parsedInfo;
21736
21787
  counts[type] = (counts[type] || 0) + 1;
@@ -21792,9 +21843,9 @@ var S3ClipsService = class {
21792
21843
  }
21793
21844
  return summary;
21794
21845
  }
21795
- const limitPerCategory = limit ? Math.min(Math.max(limit, 1), 1e3) : 30;
21846
+ const limitPerCategory = limit ? Math.min(Math.max(limit, 1), this.maxLimitPerCategory) : this.defaultLimitPerCategory;
21796
21847
  const shouldFetchAll = category === "missing_quality_check" || category === "low_value";
21797
- const initialFetchLimit = shouldFetchAll ? void 0 : category ? limitPerCategory * 3 : void 0;
21848
+ const initialFetchLimit = shouldFetchAll ? void 0 : category ? Math.min(limitPerCategory * 3, this.maxInitialFetch) : void 0;
21798
21849
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId, maxKeys: initialFetchLimit });
21799
21850
  if (s3Uris.length === 0) {
21800
21851
  console.log(`S3ClipsService: No HLS playlists found for workspace ${workspaceId} on date ${date}, shift ${shiftId}`);
@@ -21862,12 +21913,11 @@ var S3ClipsService = class {
21862
21913
  }
21863
21914
  console.log(`S3ClipsService: Total filtered URIs across all categories: ${filteredUris.length}`);
21864
21915
  }
21865
- const concurrencyLimit = 10;
21866
21916
  let processedCount = 0;
21867
21917
  const videoResults = [];
21868
21918
  console.log(`S3ClipsService: Processing ${filteredUris.length} URIs for ${category || "all categories"} with limit ${limitPerCategory} per category`);
21869
- for (let i = 0; i < filteredUris.length; i += concurrencyLimit) {
21870
- const batch = filteredUris.slice(i, i + concurrencyLimit);
21919
+ for (let i = 0; i < filteredUris.length; i += this.concurrencyLimit) {
21920
+ const batch = filteredUris.slice(i, i + this.concurrencyLimit);
21871
21921
  const batchPromises = batch.map(async (uri, batchIndex) => {
21872
21922
  const index = i + batchIndex;
21873
21923
  const result = await this.processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime || false, includeMetadata || (!!timestampStart || !!timestampEnd));
@@ -21943,6 +21993,14 @@ var BottlenecksContent = ({
21943
21993
  className
21944
21994
  }) => {
21945
21995
  const dashboardConfig = useDashboardConfig();
21996
+ const sopCategories = React14__namespace.default.useMemo(() => {
21997
+ const sopConfig = dashboardConfig?.s3Config?.sopCategories;
21998
+ if (!sopConfig) return null;
21999
+ if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
22000
+ return sopConfig.workspaceOverrides[workspaceId];
22001
+ }
22002
+ return sopConfig.default;
22003
+ }, [dashboardConfig, workspaceId]);
21946
22004
  const videoRef = React14.useRef(null);
21947
22005
  const fullscreenContainerRef = React14.useRef(null);
21948
22006
  const timestampFilterRef = React14.useRef(null);
@@ -21951,7 +22009,9 @@ var BottlenecksContent = ({
21951
22009
  const [duration, setDuration] = React14.useState(0);
21952
22010
  const [isFullscreen, setIsFullscreen] = React14.useState(false);
21953
22011
  const [currentIndex, setCurrentIndex] = React14.useState(0);
21954
- const [activeFilter, setActiveFilter] = React14.useState("low_value");
22012
+ const [activeFilter, setActiveFilter] = React14.useState(
22013
+ sopCategories && sopCategories.length > 0 ? sopCategories[0].id : "low_value"
22014
+ );
21955
22015
  const [allVideos, setAllVideos] = React14.useState([]);
21956
22016
  const [isLoading, setIsLoading] = React14.useState(true);
21957
22017
  const [error, setError] = React14.useState(null);
@@ -22063,27 +22123,41 @@ var BottlenecksContent = ({
22063
22123
  let filtered = [];
22064
22124
  if (activeFilter === "all") {
22065
22125
  filtered = [...allVideos];
22066
- } else if (activeFilter === "low_value") {
22067
- filtered = allVideos.filter((video) => video.type === "low_value");
22068
- } else if (activeFilter === "sop_deviations") {
22069
- filtered = allVideos.filter((video) => video.type === "missing_quality_check");
22070
- } else if (activeFilter === "best_cycle_time") {
22071
- filtered = allVideos.filter((video) => video.type === "best_cycle_time");
22072
- } else if (activeFilter === "worst_cycle_time") {
22073
- filtered = allVideos.filter((video) => video.type === "worst_cycle_time");
22074
- } else if (activeFilter === "cycle_completions") {
22075
- filtered = allVideos.filter((video) => video.type === "cycle_completions");
22076
- } else if (activeFilter === "long_cycle_time") {
22077
- filtered = allVideos.filter(
22078
- (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22079
- );
22080
22126
  } else {
22081
- filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22127
+ if (sopCategories && sopCategories.length > 0) {
22128
+ const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
22129
+ if (selectedCategory) {
22130
+ filtered = allVideos.filter((video) => video.type === selectedCategory.id);
22131
+ if (selectedCategory.id === "long_cycle_time") {
22132
+ filtered = allVideos.filter(
22133
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22134
+ );
22135
+ }
22136
+ }
22137
+ } else {
22138
+ if (activeFilter === "low_value") {
22139
+ filtered = allVideos.filter((video) => video.type === "low_value");
22140
+ } else if (activeFilter === "sop_deviations") {
22141
+ filtered = allVideos.filter((video) => video.type === "missing_quality_check");
22142
+ } else if (activeFilter === "best_cycle_time") {
22143
+ filtered = allVideos.filter((video) => video.type === "best_cycle_time");
22144
+ } else if (activeFilter === "worst_cycle_time") {
22145
+ filtered = allVideos.filter((video) => video.type === "worst_cycle_time");
22146
+ } else if (activeFilter === "cycle_completions") {
22147
+ filtered = allVideos.filter((video) => video.type === "cycle_completions");
22148
+ } else if (activeFilter === "long_cycle_time") {
22149
+ filtered = allVideos.filter(
22150
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22151
+ );
22152
+ } else {
22153
+ filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22154
+ }
22155
+ }
22082
22156
  }
22083
22157
  return filtered.sort((a, b) => {
22084
22158
  return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
22085
22159
  });
22086
- }, [activeFilter, allVideos]);
22160
+ }, [activeFilter, allVideos, sopCategories]);
22087
22161
  React14.useEffect(() => {
22088
22162
  if (filteredVideos.length === 0) return;
22089
22163
  const upcoming = [];
@@ -22357,35 +22431,34 @@ var BottlenecksContent = ({
22357
22431
  }
22358
22432
  };
22359
22433
  const clipCounts = React14.useMemo(() => {
22360
- if (!allVideos) return {
22361
- bottlenecks: 0,
22362
- lowValue: 0,
22363
- highSeverity: 0,
22364
- mediumSeverity: 0,
22365
- lowSeverity: 0,
22366
- sopDeviations: 0,
22367
- bestCycleTimes: 0,
22368
- worstCycleTimes: 0,
22369
- longCycleTimes: 0,
22370
- cycleCompletions: 0,
22371
- total: 0
22372
- };
22373
- return {
22374
- bottlenecks: allVideos.filter((video) => video.type === "bottleneck").length,
22375
- lowValue: allVideos.filter((video) => video.type === "low_value").length,
22376
- highSeverity: allVideos.filter((video) => video.severity === "high" && video.type === "bottleneck").length,
22377
- mediumSeverity: allVideos.filter((video) => video.severity === "medium" && video.type === "bottleneck").length,
22378
- lowSeverity: allVideos.filter((video) => video.severity === "low" && video.type === "bottleneck").length,
22379
- sopDeviations: allVideos.filter((video) => video.type === "missing_quality_check").length,
22380
- bestCycleTimes: allVideos.filter((video) => video.type === "best_cycle_time").length,
22381
- worstCycleTimes: allVideos.filter((video) => video.type === "worst_cycle_time").length,
22382
- longCycleTimes: allVideos.filter(
22434
+ if (!allVideos) return {};
22435
+ const counts = { total: allVideos.length };
22436
+ if (sopCategories && sopCategories.length > 0) {
22437
+ sopCategories.forEach((category) => {
22438
+ if (category.id === "long_cycle_time") {
22439
+ counts[category.id] = allVideos.filter(
22440
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22441
+ ).length;
22442
+ } else {
22443
+ counts[category.id] = allVideos.filter((video) => video.type === category.id).length;
22444
+ }
22445
+ });
22446
+ } else {
22447
+ counts.bottlenecks = allVideos.filter((video) => video.type === "bottleneck").length;
22448
+ counts.lowValue = allVideos.filter((video) => video.type === "low_value").length;
22449
+ counts.highSeverity = allVideos.filter((video) => video.severity === "high" && video.type === "bottleneck").length;
22450
+ counts.mediumSeverity = allVideos.filter((video) => video.severity === "medium" && video.type === "bottleneck").length;
22451
+ counts.lowSeverity = allVideos.filter((video) => video.severity === "low" && video.type === "bottleneck").length;
22452
+ counts.sopDeviations = allVideos.filter((video) => video.type === "missing_quality_check").length;
22453
+ counts.bestCycleTimes = allVideos.filter((video) => video.type === "best_cycle_time").length;
22454
+ counts.worstCycleTimes = allVideos.filter((video) => video.type === "worst_cycle_time").length;
22455
+ counts.longCycleTimes = allVideos.filter(
22383
22456
  (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22384
- ).length,
22385
- cycleCompletions: allVideos.filter((video) => video.type === "cycle_completions").length,
22386
- total: allVideos.length
22387
- };
22388
- }, [allVideos]);
22457
+ ).length;
22458
+ counts.cycleCompletions = allVideos.filter((video) => video.type === "cycle_completions").length;
22459
+ }
22460
+ return counts;
22461
+ }, [allVideos, sopCategories]);
22389
22462
  const currentVideo = React14.useMemo(() => {
22390
22463
  if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
22391
22464
  return null;
@@ -22407,9 +22480,22 @@ var BottlenecksContent = ({
22407
22480
  return "Cycle Completion";
22408
22481
  case "bottleneck":
22409
22482
  default:
22410
- return "Bottleneck";
22483
+ return "";
22411
22484
  }
22412
22485
  };
22486
+ const getColorClasses = (color2) => {
22487
+ const colorMap = {
22488
+ purple: { text: "text-purple-500", bg: "bg-purple-500", dot: "bg-purple-500" },
22489
+ green: { text: "text-green-600", bg: "bg-green-600", dot: "bg-green-600" },
22490
+ red: { text: "text-red-700", bg: "bg-red-700", dot: "bg-red-700" },
22491
+ "red-dark": { text: "text-red-500", bg: "bg-red-500", dot: "bg-red-500" },
22492
+ blue: { text: "text-blue-600", bg: "bg-blue-600", dot: "bg-blue-600" },
22493
+ orange: { text: "text-orange-600", bg: "bg-orange-600", dot: "bg-orange-600" },
22494
+ yellow: { text: "text-yellow-600", bg: "bg-yellow-600", dot: "bg-yellow-600" },
22495
+ gray: { text: "text-gray-600", bg: "bg-gray-600", dot: "bg-gray-600" }
22496
+ };
22497
+ return colorMap[color2] || colorMap.gray;
22498
+ };
22413
22499
  const formatTimeOnly = (time2) => {
22414
22500
  if (!time2) return "";
22415
22501
  try {
@@ -22439,162 +22525,62 @@ var BottlenecksContent = ({
22439
22525
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error })
22440
22526
  ] });
22441
22527
  }
22528
+ const categoriesToShow = sopCategories && sopCategories.length > 0 ? sopCategories : [
22529
+ // Default hardcoded categories if no configuration
22530
+ { id: "low_value", label: "Idle Moments", color: "purple", subtitle: "Idle time detected" },
22531
+ { id: "best_cycle_time", label: "Best Cycle Time", color: "green", subtitle: "Fastest cycle today" },
22532
+ { id: "worst_cycle_time", label: "Worst Cycle Time", color: "red", subtitle: "Slowest cycle today" },
22533
+ { id: "long_cycle_time", label: "Long Cycle Time", color: "red-dark", subtitle: "Above standard cycle times" },
22534
+ { id: "cycle_completions", label: "Cycle Completions", color: "blue", subtitle: "Completed production cycles" }
22535
+ ];
22442
22536
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-1.5 sm:p-2 lg:p-4 h-[calc(100vh-12rem)]", children: [
22443
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-3 mb-4", children: [
22444
- /* @__PURE__ */ jsxRuntime.jsxs(
22445
- Card2,
22446
- {
22447
- onClick: () => {
22448
- setActiveFilter("low_value");
22449
- trackCoreEvent("Idle Moments Filter Clicked", {
22450
- workspaceId,
22451
- workspaceName,
22452
- date,
22453
- filterType: "low_value",
22454
- clipCount: clipCounts.lowValue
22455
- });
22456
- },
22457
- 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" : ""}`,
22458
- "aria-label": `Filter by Idle Moments (${clipCounts.lowValue} clips)`,
22459
- role: "button",
22460
- tabIndex: 0,
22461
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("low_value"),
22462
- children: [
22463
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: `text-lg ${activeFilter === "low_value" ? "text-blue-600" : ""}`, children: "Idle Moments" }) }),
22464
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-center", children: [
22465
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl font-bold text-purple-500", children: clipCounts.lowValue }),
22466
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22467
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-purple-500 mr-1.5" }),
22468
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Idle time detected" })
22469
- ] })
22470
- ] }) })
22471
- ]
22472
- }
22473
- ),
22474
- /* @__PURE__ */ jsxRuntime.jsxs(
22475
- Card2,
22476
- {
22477
- onClick: () => {
22478
- setActiveFilter("best_cycle_time");
22479
- trackCoreEvent("Best Cycle Time Filter Clicked", {
22480
- workspaceId,
22481
- workspaceName,
22482
- date,
22483
- filterType: "best_cycle_time",
22484
- clipCount: clipCounts.bestCycleTimes
22485
- });
22486
- },
22487
- 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" : ""}`,
22488
- "aria-label": `Filter by Best Cycle Time (${clipCounts.bestCycleTimes} clips)`,
22489
- role: "button",
22490
- tabIndex: 0,
22491
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("best_cycle_time"),
22492
- children: [
22493
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: `text-lg ${activeFilter === "best_cycle_time" ? "text-blue-600" : ""}`, children: "Best Cycle Time" }) }),
22494
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-center", children: [
22495
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl font-bold text-green-600", children: clipCounts.bestCycleTimes }),
22496
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22497
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-green-600 mr-1.5" }),
22498
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Fastest cycle today" })
22499
- ] })
22500
- ] }) })
22501
- ]
22502
- }
22503
- ),
22504
- /* @__PURE__ */ jsxRuntime.jsxs(
22505
- Card2,
22506
- {
22507
- onClick: () => {
22508
- setActiveFilter("worst_cycle_time");
22509
- trackCoreEvent("Worst Cycle Time Filter Clicked", {
22510
- workspaceId,
22511
- workspaceName,
22512
- date,
22513
- filterType: "worst_cycle_time",
22514
- clipCount: clipCounts.worstCycleTimes
22515
- });
22516
- },
22517
- 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" : ""}`,
22518
- "aria-label": `Filter by Worst Cycle Time (${clipCounts.worstCycleTimes} clips)`,
22519
- role: "button",
22520
- tabIndex: 0,
22521
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("worst_cycle_time"),
22522
- children: [
22523
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: `text-lg ${activeFilter === "worst_cycle_time" ? "text-blue-600" : ""}`, children: "Worst Cycle Time" }) }),
22524
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-center", children: [
22525
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl font-bold text-red-700", children: clipCounts.worstCycleTimes }),
22526
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22527
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-red-700 mr-1.5" }),
22528
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Slowest cycle today" })
22529
- ] })
22530
- ] }) })
22531
- ]
22532
- }
22533
- ),
22534
- /* @__PURE__ */ jsxRuntime.jsxs(
22537
+ /* @__PURE__ */ jsxRuntime.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) => {
22538
+ const colorClasses = getColorClasses(category.color);
22539
+ const count = clipCounts[category.id] || 0;
22540
+ return /* @__PURE__ */ jsxRuntime.jsxs(
22535
22541
  Card2,
22536
22542
  {
22537
22543
  onClick: () => {
22538
- setActiveFilter("long_cycle_time");
22539
- trackCoreEvent("Long Cycle Time Filter Clicked", {
22544
+ setActiveFilter(category.id);
22545
+ trackCoreEvent(`${category.label} Filter Clicked`, {
22540
22546
  workspaceId,
22541
22547
  workspaceName,
22542
22548
  date,
22543
- filterType: "long_cycle_time",
22544
- clipCount: clipCounts.longCycleTimes
22549
+ filterType: category.id,
22550
+ clipCount: count
22545
22551
  });
22546
22552
  },
22547
- 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" : ""}`,
22548
- "aria-label": `Filter by Long Cycle Time Bottlenecks (${clipCounts.longCycleTimes} clips)`,
22553
+ 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" : ""}`,
22554
+ "aria-label": `Filter by ${category.label} (${count} clips)`,
22549
22555
  role: "button",
22550
22556
  tabIndex: 0,
22551
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("long_cycle_time"),
22557
+ onKeyDown: (e) => e.key === "Enter" && setActiveFilter(category.id),
22552
22558
  children: [
22553
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: `text-lg ${activeFilter === "long_cycle_time" ? "text-blue-600" : ""}`, children: "Long Cycle Time" }) }),
22559
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: `text-lg ${activeFilter === category.id ? "text-blue-600" : ""}`, children: category.label }) }),
22554
22560
  /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-center", children: [
22555
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl font-bold text-red-500", children: clipCounts.longCycleTimes }),
22561
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-3xl font-bold ${colorClasses.text}`, children: count }),
22556
22562
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22557
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-red-500 mr-1.5" }),
22558
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Above standard cycle times" })
22563
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `h-2 w-2 rounded-full ${colorClasses.dot} mr-1.5` }),
22564
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: category.subtitle || category.description || category.label })
22559
22565
  ] })
22560
22566
  ] }) })
22561
22567
  ]
22562
- }
22563
- ),
22564
- /* @__PURE__ */ jsxRuntime.jsxs(
22565
- Card2,
22566
- {
22567
- onClick: () => {
22568
- setActiveFilter("cycle_completions");
22569
- trackCoreEvent("Cycle Completions Filter Clicked", {
22570
- workspaceId,
22571
- workspaceName,
22572
- date,
22573
- filterType: "cycle_completions",
22574
- clipCount: clipCounts.cycleCompletions
22575
- });
22576
- },
22577
- 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" : ""}`,
22578
- "aria-label": `Filter by Cycle Completions (${clipCounts.cycleCompletions} clips)`,
22579
- role: "button",
22580
- tabIndex: 0,
22581
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("cycle_completions"),
22582
- children: [
22583
- /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: `text-lg ${activeFilter === "cycle_completions" ? "text-blue-600" : ""}`, children: "Cycle Completions" }) }),
22584
- /* @__PURE__ */ jsxRuntime.jsx(CardContent2, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-center", children: [
22585
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-3xl font-bold text-blue-600", children: clipCounts.cycleCompletions }),
22586
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center text-sm text-gray-500 mt-1", children: [
22587
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-blue-600 mr-1.5" }),
22588
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Completed production cycles" })
22589
- ] })
22590
- ] }) })
22591
- ]
22592
- }
22593
- )
22594
- ] }),
22568
+ },
22569
+ category.id
22570
+ );
22571
+ }) }),
22595
22572
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden", style: { height: "calc(100% - 8.5rem)" }, children: [
22596
22573
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-gray-100", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
22597
- /* @__PURE__ */ jsxRuntime.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})` }),
22574
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-800", children: (() => {
22575
+ if (activeFilter === "all") {
22576
+ return `All Clips (${clipCounts.total || 0})`;
22577
+ }
22578
+ const activeCategory = categoriesToShow.find((cat) => cat.id === activeFilter);
22579
+ if (activeCategory) {
22580
+ return `${activeCategory.label} (${clipCounts[activeCategory.id] || 0})`;
22581
+ }
22582
+ 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})`;
22583
+ })() }),
22598
22584
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
22599
22585
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: timestampFilterRef, children: [
22600
22586
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -24970,14 +24956,6 @@ var AIAgentView = () => {
24970
24956
  return newMap;
24971
24957
  });
24972
24958
  },
24973
- onReasoning: (text) => {
24974
- setStreamingStates((prev) => {
24975
- const newMap = new Map(prev);
24976
- const current = newMap.get(currentThreadId) || { message: "", reasoning: "" };
24977
- newMap.set(currentThreadId, { ...current, reasoning: current.reasoning + text });
24978
- return newMap;
24979
- });
24980
- },
24981
24959
  onComplete: async (messageId) => {
24982
24960
  if (currentThreadId && !currentThreadId.startsWith("temp-")) {
24983
24961
  const updatedMessages = await getAllThreadMessages(currentThreadId);
@@ -27366,14 +27344,20 @@ function HomeView({
27366
27344
  }) {
27367
27345
  const [isHydrated, setIsHydrated] = React14.useState(false);
27368
27346
  const availableLineIds = React14.useMemo(() => [factoryViewId, ...allLineIds], [factoryViewId, allLineIds]);
27369
- const [selectedLineId, setSelectedLineId] = React14.useState(defaultLineId);
27347
+ const [selectedLineId, setSelectedLineId] = React14.useState(factoryViewId);
27370
27348
  const [isChangingFilter, setIsChangingFilter] = React14.useState(false);
27371
27349
  const [errorMessage, setErrorMessage] = React14.useState(null);
27372
27350
  const [displayNamesInitialized, setDisplayNamesInitialized] = React14.useState(false);
27373
27351
  React14.useEffect(() => {
27374
27352
  const initDisplayNames = async () => {
27375
27353
  try {
27376
- await preInitializeWorkspaceDisplayNames(selectedLineId);
27354
+ if (selectedLineId === factoryViewId) {
27355
+ for (const lineId of allLineIds) {
27356
+ await preInitializeWorkspaceDisplayNames(lineId);
27357
+ }
27358
+ } else {
27359
+ await preInitializeWorkspaceDisplayNames(selectedLineId);
27360
+ }
27377
27361
  setDisplayNamesInitialized(true);
27378
27362
  } catch (error) {
27379
27363
  console.error("Failed to pre-initialize workspace display names:", error);
@@ -27381,7 +27365,7 @@ function HomeView({
27381
27365
  }
27382
27366
  };
27383
27367
  initDisplayNames();
27384
- }, [selectedLineId]);
27368
+ }, [selectedLineId, factoryViewId, allLineIds]);
27385
27369
  const {
27386
27370
  displayNames: workspaceDisplayNames,
27387
27371
  loading: displayNamesLoading,
@@ -27414,11 +27398,17 @@ function HomeView({
27414
27398
  lineId: selectedLineId,
27415
27399
  onLineMetricsUpdate
27416
27400
  });
27401
+ const lineIdsForBreaks = React14.useMemo(() => {
27402
+ if (selectedLineId === factoryViewId) {
27403
+ return allLineIds;
27404
+ }
27405
+ return [selectedLineId];
27406
+ }, [selectedLineId, factoryViewId, allLineIds]);
27417
27407
  const {
27418
27408
  activeBreaks,
27419
27409
  isLoading: breaksLoading,
27420
27410
  error: breaksError
27421
- } = useActiveBreaks([selectedLineId]);
27411
+ } = useActiveBreaks(lineIdsForBreaks);
27422
27412
  const memoizedWorkspaceMetrics = React14.useMemo(() => workspaceMetrics, [
27423
27413
  // Only update reference if meaningful properties change
27424
27414
  workspaceMetrics.length,
@@ -27461,10 +27451,15 @@ function HomeView({
27461
27451
  const lineTitle = React14.useMemo(() => {
27462
27452
  return factoryName;
27463
27453
  }, [factoryName]);
27464
- const lineSelectorComponent = React14.useMemo(() => /* @__PURE__ */ jsxRuntime.jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
27465
- /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-9 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Select a line" }) }),
27466
- /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md", children: availableLineIds.map((id3) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: id3, children: lineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
27467
- ] }), [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId]);
27454
+ const lineSelectorComponent = React14.useMemo(() => {
27455
+ if (allLineIds.length <= 1) {
27456
+ return null;
27457
+ }
27458
+ return /* @__PURE__ */ jsxRuntime.jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
27459
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "w-full sm:w-[200px] bg-white border border-gray-200 shadow-sm rounded-md h-9 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Select a line" }) }),
27460
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { className: "z-50 bg-white shadow-lg border border-gray-200 rounded-md", children: availableLineIds.map((id3) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: id3, children: lineNames[id3] || (id3 === factoryViewId ? "All Lines" : `Line ${id3.substring(0, 4)}`) }, id3)) })
27461
+ ] });
27462
+ }, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
27468
27463
  const isLoading = !isHydrated || metricsLoading || kpisLoading || isChangingFilter || displayNamesLoading || !displayNamesInitialized;
27469
27464
  if (isLoading) {
27470
27465
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen bg-slate-50", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingPageCmp, { message: "Loading dashboard..." }) });
@@ -27500,7 +27495,7 @@ function HomeView({
27500
27495
  }
27501
27496
  ) }) }),
27502
27497
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-y-auto sm:overflow-hidden relative", children: [
27503
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
27498
+ lineSelectorComponent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
27504
27499
  memoizedWorkspaceMetrics.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full sm:h-full min-h-[calc(100vh-80px)] sm:min-h-0", children: React14__namespace.default.createElement(WorkspaceGrid, {
27505
27500
  workspaces: memoizedWorkspaceMetrics,
27506
27501
  lineNames,
@@ -28680,8 +28675,7 @@ var MobileWorkspaceCard = React14.memo(({
28680
28675
  cardClass,
28681
28676
  onWorkspaceClick,
28682
28677
  getMedalIcon,
28683
- line1Id,
28684
- line2Id
28678
+ getLineName
28685
28679
  }) => /* @__PURE__ */ jsxRuntime.jsxs(
28686
28680
  "div",
28687
28681
  {
@@ -28699,7 +28693,7 @@ var MobileWorkspaceCard = React14.memo(({
28699
28693
  ] }),
28700
28694
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
28701
28695
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children: getWorkspaceDisplayName(workspace.workspace_name) }),
28702
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: workspace.line_id === line1Id ? "Line 1" : workspace.line_id === line2Id ? "Line 2" : workspace.line_id })
28696
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: getLineName(workspace.line_id) })
28703
28697
  ] })
28704
28698
  ] }),
28705
28699
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
@@ -28737,8 +28731,7 @@ var DesktopWorkspaceRow = React14.memo(({
28737
28731
  rowClass,
28738
28732
  onWorkspaceClick,
28739
28733
  getMedalIcon,
28740
- line1Id,
28741
- line2Id
28734
+ getLineName
28742
28735
  }) => /* @__PURE__ */ jsxRuntime.jsxs(
28743
28736
  "tr",
28744
28737
  {
@@ -28750,7 +28743,7 @@ var DesktopWorkspaceRow = React14.memo(({
28750
28743
  getMedalIcon(index + 1)
28751
28744
  ] }) }),
28752
28745
  /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: getWorkspaceDisplayName(workspace.workspace_name) }) }),
28753
- /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.line_id === line1Id ? "Line 1" : workspace.line_id === line2Id ? "Line 2" : workspace.line_id }) }),
28746
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: getLineName(workspace.line_id) }) }),
28754
28747
  /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium whitespace-nowrap", children: [
28755
28748
  (workspace.efficiency || 0).toFixed(1),
28756
28749
  "%"
@@ -28778,10 +28771,19 @@ var LeaderboardDetailView = React14.memo(({
28778
28771
  onWorkspaceClick,
28779
28772
  line1Id = "",
28780
28773
  line2Id = "",
28774
+ lineNames = {},
28781
28775
  className = ""
28782
28776
  }) => {
28783
28777
  const navigation = useNavigation();
28784
28778
  const [sortAscending, setSortAscending] = React14.useState(false);
28779
+ const getLineName = React14.useCallback((lineId2) => {
28780
+ if (lineNames[lineId2]) {
28781
+ return lineNames[lineId2];
28782
+ }
28783
+ if (lineId2 === line1Id) return "Line 1";
28784
+ if (lineId2 === line2Id) return "Line 2";
28785
+ return lineId2;
28786
+ }, [lineNames, line1Id, line2Id]);
28785
28787
  const handleSortToggle = React14.useCallback(() => {
28786
28788
  setSortAscending(!sortAscending);
28787
28789
  }, [sortAscending]);
@@ -28955,8 +28957,7 @@ var LeaderboardDetailView = React14.memo(({
28955
28957
  cardClass,
28956
28958
  onWorkspaceClick: handleWorkspaceClick,
28957
28959
  getMedalIcon,
28958
- line1Id,
28959
- line2Id
28960
+ getLineName
28960
28961
  },
28961
28962
  ws.workspace_uuid
28962
28963
  );
@@ -28981,8 +28982,7 @@ var LeaderboardDetailView = React14.memo(({
28981
28982
  rowClass,
28982
28983
  onWorkspaceClick: handleWorkspaceClick,
28983
28984
  getMedalIcon,
28984
- line1Id,
28985
- line2Id
28985
+ getLineName
28986
28986
  },
28987
28987
  ws.workspace_uuid
28988
28988
  );
@@ -28991,7 +28991,7 @@ var LeaderboardDetailView = React14.memo(({
28991
28991
  ] })
28992
28992
  ] });
28993
28993
  }, (prevProps, nextProps) => {
28994
- 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;
28994
+ 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;
28995
28995
  });
28996
28996
  LeaderboardDetailView.displayName = "LeaderboardDetailView";
28997
28997
  var LeaderboardDetailView_default = LeaderboardDetailView;