@optifye/dashboard-core 6.0.0 → 6.0.1

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
@@ -1027,7 +1027,7 @@ var dashboardService = {
1027
1027
  const formattedStartDate = formatDate(startDate);
1028
1028
  const formattedEndDate = formatDate(endDate);
1029
1029
  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 });
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, idle_time").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1031
1031
  if (error) throw error;
1032
1032
  if (!data) return [];
1033
1033
  const transformedData = data.map((item) => ({
@@ -1040,7 +1040,8 @@ var dashboardService = {
1040
1040
  ideal_output: item.ideal_output || 0,
1041
1041
  avg_pph: item.avg_pph || 0,
1042
1042
  pph_threshold: item.pph_threshold || 0,
1043
- workspace_rank: item.workspace_rank || 0
1043
+ workspace_rank: item.workspace_rank || 0,
1044
+ idle_time: item.idle_time || 0
1044
1045
  }));
1045
1046
  return transformedData;
1046
1047
  } catch (err) {
@@ -17520,7 +17521,6 @@ var VideoCard = React14__namespace.default.memo(({
17520
17521
  });
17521
17522
  }
17522
17523
  const displayName = getWorkspaceDisplayName(workspace.workspace_name);
17523
- workspace.workspace_uuid || workspace.workspace_name;
17524
17524
  const getEfficiencyOverlayColor = (efficiency) => {
17525
17525
  if (efficiency >= 80) {
17526
17526
  return "bg-[#00D654]/25";
@@ -17648,7 +17648,6 @@ var VideoGridView = React14__namespace.default.memo(({
17648
17648
  workspaces,
17649
17649
  selectedLine,
17650
17650
  className = "",
17651
- lineIdMapping = {},
17652
17651
  videoSources = {}
17653
17652
  }) => {
17654
17653
  const router$1 = router.useRouter();
@@ -17797,16 +17796,17 @@ var VideoGridView = React14__namespace.default.memo(({
17797
17796
  minHeight: "100%"
17798
17797
  },
17799
17798
  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
- }
17799
+ if (a.line_id !== b.line_id) {
17800
+ return (a.line_id || "").localeCompare(b.line_id || "");
17801
+ }
17802
+ const aMatch = a.workspace_name.match(/WS(\d+)/);
17803
+ const bMatch = b.workspace_name.match(/WS(\d+)/);
17804
+ if (aMatch && bMatch) {
17805
+ const aNum = parseInt(aMatch[1]);
17806
+ const bNum = parseInt(bMatch[1]);
17807
+ return aNum - bNum;
17808
17808
  }
17809
- return aNum - bNum;
17809
+ return a.workspace_name.localeCompare(b.workspace_name);
17810
17810
  }).map((workspace) => {
17811
17811
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
17812
17812
  const isVisible = visibleWorkspaces.has(workspaceId);
@@ -21394,7 +21394,7 @@ function isValidShiftId(shiftId) {
21394
21394
  }
21395
21395
 
21396
21396
  // src/lib/api/s3-clips-parser.ts
21397
- function parseS3Uri(s3Uri) {
21397
+ function parseS3Uri(s3Uri, sopCategories) {
21398
21398
  const path = new URL(s3Uri).pathname;
21399
21399
  const parts = path.split("/").filter((p) => p);
21400
21400
  console.log("S3 URI:", s3Uri);
@@ -21434,6 +21434,27 @@ function parseS3Uri(s3Uri) {
21434
21434
  let description = "Analysis Clip";
21435
21435
  const normalizedViolationType = violationType.toLowerCase().trim();
21436
21436
  console.log(`Parsing violation type: "${violationType}" (normalized: "${normalizedViolationType}") from path: ${s3Uri}`);
21437
+ if (sopCategories && sopCategories.length > 0) {
21438
+ const matchedCategory = sopCategories.find((category) => {
21439
+ const categoryId = category.id.toLowerCase();
21440
+ const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
21441
+ return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
21442
+ normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
21443
+ });
21444
+ if (matchedCategory) {
21445
+ type = matchedCategory.id;
21446
+ description = matchedCategory.description || matchedCategory.label;
21447
+ if (matchedCategory.color.includes("red")) {
21448
+ severity = "high";
21449
+ } else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
21450
+ severity = "medium";
21451
+ } else {
21452
+ severity = "low";
21453
+ }
21454
+ console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
21455
+ return { timestamp, severity, description, type, originalUri: s3Uri };
21456
+ }
21457
+ }
21437
21458
  switch (normalizedViolationType) {
21438
21459
  case "idle_time":
21439
21460
  case "idle":
@@ -21673,11 +21694,23 @@ var S3ClipsService = class {
21673
21694
  const key = url.pathname.startsWith("/") ? url.pathname.substring(1) : url.pathname;
21674
21695
  return `${this.config.s3Config.cloudFrontDomain}/${key}`;
21675
21696
  }
21697
+ /**
21698
+ * Gets SOP categories for a specific workspace
21699
+ */
21700
+ getSOPCategories(workspaceId) {
21701
+ const sopConfig = this.config.s3Config?.sopCategories;
21702
+ if (!sopConfig) return void 0;
21703
+ if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
21704
+ return sopConfig.workspaceOverrides[workspaceId];
21705
+ }
21706
+ return sopConfig.default;
21707
+ }
21676
21708
  /**
21677
21709
  * Processes a single video completely
21678
21710
  */
21679
21711
  async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
21680
- const parsedInfo = parseS3Uri(uri);
21712
+ const sopCategories = this.getSOPCategories(workspaceId);
21713
+ const parsedInfo = parseS3Uri(uri, sopCategories);
21681
21714
  if (!parsedInfo) {
21682
21715
  console.warn(`Skipping URI due to parsing failure: ${uri}`);
21683
21716
  return null;
@@ -21710,27 +21743,32 @@ var S3ClipsService = class {
21710
21743
  async getVideoSummary(workspaceId, date, shiftId) {
21711
21744
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId });
21712
21745
  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
- };
21746
+ const sopCategories = this.getSOPCategories(workspaceId);
21747
+ const counts = { total: 0 };
21748
+ const samples = {};
21749
+ if (sopCategories && sopCategories.length > 0) {
21750
+ sopCategories.forEach((category) => {
21751
+ counts[category.id] = 0;
21752
+ samples[category.id] = null;
21753
+ });
21754
+ } else {
21755
+ counts.best_cycle_time = 0;
21756
+ counts.worst_cycle_time = 0;
21757
+ counts.bottleneck = 0;
21758
+ counts.low_value = 0;
21759
+ counts.long_cycle_time = 0;
21760
+ counts.missing_quality_check = 0;
21761
+ counts.cycle_completions = 0;
21762
+ samples.best_cycle_time = null;
21763
+ samples.worst_cycle_time = null;
21764
+ samples.bottleneck = null;
21765
+ samples.low_value = null;
21766
+ samples.long_cycle_time = null;
21767
+ samples.missing_quality_check = null;
21768
+ samples.cycle_completions = null;
21769
+ }
21732
21770
  for (const uri of s3Uris) {
21733
- const parsedInfo = parseS3Uri(uri);
21771
+ const parsedInfo = parseS3Uri(uri, sopCategories);
21734
21772
  if (parsedInfo) {
21735
21773
  const { type } = parsedInfo;
21736
21774
  counts[type] = (counts[type] || 0) + 1;
@@ -21943,6 +21981,14 @@ var BottlenecksContent = ({
21943
21981
  className
21944
21982
  }) => {
21945
21983
  const dashboardConfig = useDashboardConfig();
21984
+ const sopCategories = React14__namespace.default.useMemo(() => {
21985
+ const sopConfig = dashboardConfig?.s3Config?.sopCategories;
21986
+ if (!sopConfig) return null;
21987
+ if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
21988
+ return sopConfig.workspaceOverrides[workspaceId];
21989
+ }
21990
+ return sopConfig.default;
21991
+ }, [dashboardConfig, workspaceId]);
21946
21992
  const videoRef = React14.useRef(null);
21947
21993
  const fullscreenContainerRef = React14.useRef(null);
21948
21994
  const timestampFilterRef = React14.useRef(null);
@@ -21951,7 +21997,9 @@ var BottlenecksContent = ({
21951
21997
  const [duration, setDuration] = React14.useState(0);
21952
21998
  const [isFullscreen, setIsFullscreen] = React14.useState(false);
21953
21999
  const [currentIndex, setCurrentIndex] = React14.useState(0);
21954
- const [activeFilter, setActiveFilter] = React14.useState("low_value");
22000
+ const [activeFilter, setActiveFilter] = React14.useState(
22001
+ sopCategories && sopCategories.length > 0 ? sopCategories[0].id : "low_value"
22002
+ );
21955
22003
  const [allVideos, setAllVideos] = React14.useState([]);
21956
22004
  const [isLoading, setIsLoading] = React14.useState(true);
21957
22005
  const [error, setError] = React14.useState(null);
@@ -22063,27 +22111,41 @@ var BottlenecksContent = ({
22063
22111
  let filtered = [];
22064
22112
  if (activeFilter === "all") {
22065
22113
  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
22114
  } else {
22081
- filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22115
+ if (sopCategories && sopCategories.length > 0) {
22116
+ const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
22117
+ if (selectedCategory) {
22118
+ filtered = allVideos.filter((video) => video.type === selectedCategory.id);
22119
+ if (selectedCategory.id === "long_cycle_time") {
22120
+ filtered = allVideos.filter(
22121
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22122
+ );
22123
+ }
22124
+ }
22125
+ } else {
22126
+ if (activeFilter === "low_value") {
22127
+ filtered = allVideos.filter((video) => video.type === "low_value");
22128
+ } else if (activeFilter === "sop_deviations") {
22129
+ filtered = allVideos.filter((video) => video.type === "missing_quality_check");
22130
+ } else if (activeFilter === "best_cycle_time") {
22131
+ filtered = allVideos.filter((video) => video.type === "best_cycle_time");
22132
+ } else if (activeFilter === "worst_cycle_time") {
22133
+ filtered = allVideos.filter((video) => video.type === "worst_cycle_time");
22134
+ } else if (activeFilter === "cycle_completions") {
22135
+ filtered = allVideos.filter((video) => video.type === "cycle_completions");
22136
+ } else if (activeFilter === "long_cycle_time") {
22137
+ filtered = allVideos.filter(
22138
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22139
+ );
22140
+ } else {
22141
+ filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22142
+ }
22143
+ }
22082
22144
  }
22083
22145
  return filtered.sort((a, b) => {
22084
22146
  return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
22085
22147
  });
22086
- }, [activeFilter, allVideos]);
22148
+ }, [activeFilter, allVideos, sopCategories]);
22087
22149
  React14.useEffect(() => {
22088
22150
  if (filteredVideos.length === 0) return;
22089
22151
  const upcoming = [];
@@ -22357,35 +22419,34 @@ var BottlenecksContent = ({
22357
22419
  }
22358
22420
  };
22359
22421
  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(
22422
+ if (!allVideos) return {};
22423
+ const counts = { total: allVideos.length };
22424
+ if (sopCategories && sopCategories.length > 0) {
22425
+ sopCategories.forEach((category) => {
22426
+ if (category.id === "long_cycle_time") {
22427
+ counts[category.id] = allVideos.filter(
22428
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22429
+ ).length;
22430
+ } else {
22431
+ counts[category.id] = allVideos.filter((video) => video.type === category.id).length;
22432
+ }
22433
+ });
22434
+ } else {
22435
+ counts.bottlenecks = allVideos.filter((video) => video.type === "bottleneck").length;
22436
+ counts.lowValue = allVideos.filter((video) => video.type === "low_value").length;
22437
+ counts.highSeverity = allVideos.filter((video) => video.severity === "high" && video.type === "bottleneck").length;
22438
+ counts.mediumSeverity = allVideos.filter((video) => video.severity === "medium" && video.type === "bottleneck").length;
22439
+ counts.lowSeverity = allVideos.filter((video) => video.severity === "low" && video.type === "bottleneck").length;
22440
+ counts.sopDeviations = allVideos.filter((video) => video.type === "missing_quality_check").length;
22441
+ counts.bestCycleTimes = allVideos.filter((video) => video.type === "best_cycle_time").length;
22442
+ counts.worstCycleTimes = allVideos.filter((video) => video.type === "worst_cycle_time").length;
22443
+ counts.longCycleTimes = allVideos.filter(
22383
22444
  (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]);
22445
+ ).length;
22446
+ counts.cycleCompletions = allVideos.filter((video) => video.type === "cycle_completions").length;
22447
+ }
22448
+ return counts;
22449
+ }, [allVideos, sopCategories]);
22389
22450
  const currentVideo = React14.useMemo(() => {
22390
22451
  if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
22391
22452
  return null;
@@ -22410,6 +22471,19 @@ var BottlenecksContent = ({
22410
22471
  return "Bottleneck";
22411
22472
  }
22412
22473
  };
22474
+ const getColorClasses = (color2) => {
22475
+ const colorMap = {
22476
+ purple: { text: "text-purple-500", bg: "bg-purple-500", dot: "bg-purple-500" },
22477
+ green: { text: "text-green-600", bg: "bg-green-600", dot: "bg-green-600" },
22478
+ red: { text: "text-red-700", bg: "bg-red-700", dot: "bg-red-700" },
22479
+ "red-dark": { text: "text-red-500", bg: "bg-red-500", dot: "bg-red-500" },
22480
+ blue: { text: "text-blue-600", bg: "bg-blue-600", dot: "bg-blue-600" },
22481
+ orange: { text: "text-orange-600", bg: "bg-orange-600", dot: "bg-orange-600" },
22482
+ yellow: { text: "text-yellow-600", bg: "bg-yellow-600", dot: "bg-yellow-600" },
22483
+ gray: { text: "text-gray-600", bg: "bg-gray-600", dot: "bg-gray-600" }
22484
+ };
22485
+ return colorMap[color2] || colorMap.gray;
22486
+ };
22413
22487
  const formatTimeOnly = (time2) => {
22414
22488
  if (!time2) return "";
22415
22489
  try {
@@ -22439,162 +22513,62 @@ var BottlenecksContent = ({
22439
22513
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error })
22440
22514
  ] });
22441
22515
  }
22516
+ const categoriesToShow = sopCategories && sopCategories.length > 0 ? sopCategories : [
22517
+ // Default hardcoded categories if no configuration
22518
+ { id: "low_value", label: "Idle Moments", color: "purple", subtitle: "Idle time detected" },
22519
+ { id: "best_cycle_time", label: "Best Cycle Time", color: "green", subtitle: "Fastest cycle today" },
22520
+ { id: "worst_cycle_time", label: "Worst Cycle Time", color: "red", subtitle: "Slowest cycle today" },
22521
+ { id: "long_cycle_time", label: "Long Cycle Time", color: "red-dark", subtitle: "Above standard cycle times" },
22522
+ { id: "cycle_completions", label: "Cycle Completions", color: "blue", subtitle: "Completed production cycles" }
22523
+ ];
22442
22524
  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(
22525
+ /* @__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) => {
22526
+ const colorClasses = getColorClasses(category.color);
22527
+ const count = clipCounts[category.id] || 0;
22528
+ return /* @__PURE__ */ jsxRuntime.jsxs(
22505
22529
  Card2,
22506
22530
  {
22507
22531
  onClick: () => {
22508
- setActiveFilter("worst_cycle_time");
22509
- trackCoreEvent("Worst Cycle Time Filter Clicked", {
22532
+ setActiveFilter(category.id);
22533
+ trackCoreEvent(`${category.label} Filter Clicked`, {
22510
22534
  workspaceId,
22511
22535
  workspaceName,
22512
22536
  date,
22513
- filterType: "worst_cycle_time",
22514
- clipCount: clipCounts.worstCycleTimes
22537
+ filterType: category.id,
22538
+ clipCount: count
22515
22539
  });
22516
22540
  },
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)`,
22541
+ 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" : ""}`,
22542
+ "aria-label": `Filter by ${category.label} (${count} clips)`,
22519
22543
  role: "button",
22520
22544
  tabIndex: 0,
22521
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("worst_cycle_time"),
22545
+ onKeyDown: (e) => e.key === "Enter" && setActiveFilter(category.id),
22522
22546
  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" }) }),
22547
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: `text-lg ${activeFilter === category.id ? "text-blue-600" : ""}`, children: category.label }) }),
22524
22548
  /* @__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 }),
22549
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-3xl font-bold ${colorClasses.text}`, children: count }),
22526
22550
  /* @__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" })
22551
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `h-2 w-2 rounded-full ${colorClasses.dot} mr-1.5` }),
22552
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: category.subtitle || category.description || category.label })
22529
22553
  ] })
22530
22554
  ] }) })
22531
22555
  ]
22532
- }
22533
- ),
22534
- /* @__PURE__ */ jsxRuntime.jsxs(
22535
- Card2,
22536
- {
22537
- onClick: () => {
22538
- setActiveFilter("long_cycle_time");
22539
- trackCoreEvent("Long Cycle Time Filter Clicked", {
22540
- workspaceId,
22541
- workspaceName,
22542
- date,
22543
- filterType: "long_cycle_time",
22544
- clipCount: clipCounts.longCycleTimes
22545
- });
22546
- },
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)`,
22549
- role: "button",
22550
- tabIndex: 0,
22551
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("long_cycle_time"),
22552
- 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" }) }),
22554
- /* @__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 }),
22556
- /* @__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" })
22559
- ] })
22560
- ] }) })
22561
- ]
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
- ] }),
22556
+ },
22557
+ category.id
22558
+ );
22559
+ }) }),
22595
22560
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden", style: { height: "calc(100% - 8.5rem)" }, children: [
22596
22561
  /* @__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})` }),
22562
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-gray-800", children: (() => {
22563
+ if (activeFilter === "all") {
22564
+ return `All Clips (${clipCounts.total || 0})`;
22565
+ }
22566
+ const activeCategory = categoriesToShow.find((cat) => cat.id === activeFilter);
22567
+ if (activeCategory) {
22568
+ return `${activeCategory.label} (${clipCounts[activeCategory.id] || 0})`;
22569
+ }
22570
+ 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})`;
22571
+ })() }),
22598
22572
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
22599
22573
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: timestampFilterRef, children: [
22600
22574
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -24970,14 +24944,6 @@ var AIAgentView = () => {
24970
24944
  return newMap;
24971
24945
  });
24972
24946
  },
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
24947
  onComplete: async (messageId) => {
24982
24948
  if (currentThreadId && !currentThreadId.startsWith("temp-")) {
24983
24949
  const updatedMessages = await getAllThreadMessages(currentThreadId);
@@ -27366,14 +27332,20 @@ function HomeView({
27366
27332
  }) {
27367
27333
  const [isHydrated, setIsHydrated] = React14.useState(false);
27368
27334
  const availableLineIds = React14.useMemo(() => [factoryViewId, ...allLineIds], [factoryViewId, allLineIds]);
27369
- const [selectedLineId, setSelectedLineId] = React14.useState(defaultLineId);
27335
+ const [selectedLineId, setSelectedLineId] = React14.useState(factoryViewId);
27370
27336
  const [isChangingFilter, setIsChangingFilter] = React14.useState(false);
27371
27337
  const [errorMessage, setErrorMessage] = React14.useState(null);
27372
27338
  const [displayNamesInitialized, setDisplayNamesInitialized] = React14.useState(false);
27373
27339
  React14.useEffect(() => {
27374
27340
  const initDisplayNames = async () => {
27375
27341
  try {
27376
- await preInitializeWorkspaceDisplayNames(selectedLineId);
27342
+ if (selectedLineId === factoryViewId) {
27343
+ for (const lineId of allLineIds) {
27344
+ await preInitializeWorkspaceDisplayNames(lineId);
27345
+ }
27346
+ } else {
27347
+ await preInitializeWorkspaceDisplayNames(selectedLineId);
27348
+ }
27377
27349
  setDisplayNamesInitialized(true);
27378
27350
  } catch (error) {
27379
27351
  console.error("Failed to pre-initialize workspace display names:", error);
@@ -27381,7 +27353,7 @@ function HomeView({
27381
27353
  }
27382
27354
  };
27383
27355
  initDisplayNames();
27384
- }, [selectedLineId]);
27356
+ }, [selectedLineId, factoryViewId, allLineIds]);
27385
27357
  const {
27386
27358
  displayNames: workspaceDisplayNames,
27387
27359
  loading: displayNamesLoading,
@@ -27461,10 +27433,15 @@ function HomeView({
27461
27433
  const lineTitle = React14.useMemo(() => {
27462
27434
  return factoryName;
27463
27435
  }, [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]);
27436
+ const lineSelectorComponent = React14.useMemo(() => {
27437
+ if (allLineIds.length <= 1) {
27438
+ return null;
27439
+ }
27440
+ return /* @__PURE__ */ jsxRuntime.jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
27441
+ /* @__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" }) }),
27442
+ /* @__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)) })
27443
+ ] });
27444
+ }, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
27468
27445
  const isLoading = !isHydrated || metricsLoading || kpisLoading || isChangingFilter || displayNamesLoading || !displayNamesInitialized;
27469
27446
  if (isLoading) {
27470
27447
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-screen bg-slate-50", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingPageCmp, { message: "Loading dashboard..." }) });
@@ -27500,7 +27477,7 @@ function HomeView({
27500
27477
  }
27501
27478
  ) }) }),
27502
27479
  /* @__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 }),
27480
+ lineSelectorComponent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
27504
27481
  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
27482
  workspaces: memoizedWorkspaceMetrics,
27506
27483
  lineNames,