@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.mjs CHANGED
@@ -998,7 +998,7 @@ var dashboardService = {
998
998
  const formattedStartDate = formatDate(startDate);
999
999
  const formattedEndDate = formatDate(endDate);
1000
1000
  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 });
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, idle_time").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
1002
1002
  if (error) throw error;
1003
1003
  if (!data) return [];
1004
1004
  const transformedData = data.map((item) => ({
@@ -1011,7 +1011,8 @@ var dashboardService = {
1011
1011
  ideal_output: item.ideal_output || 0,
1012
1012
  avg_pph: item.avg_pph || 0,
1013
1013
  pph_threshold: item.pph_threshold || 0,
1014
- workspace_rank: item.workspace_rank || 0
1014
+ workspace_rank: item.workspace_rank || 0,
1015
+ idle_time: item.idle_time || 0
1015
1016
  }));
1016
1017
  return transformedData;
1017
1018
  } catch (err) {
@@ -17491,7 +17492,6 @@ var VideoCard = React14__default.memo(({
17491
17492
  });
17492
17493
  }
17493
17494
  const displayName = getWorkspaceDisplayName(workspace.workspace_name);
17494
- workspace.workspace_uuid || workspace.workspace_name;
17495
17495
  const getEfficiencyOverlayColor = (efficiency) => {
17496
17496
  if (efficiency >= 80) {
17497
17497
  return "bg-[#00D654]/25";
@@ -17619,7 +17619,6 @@ var VideoGridView = React14__default.memo(({
17619
17619
  workspaces,
17620
17620
  selectedLine,
17621
17621
  className = "",
17622
- lineIdMapping = {},
17623
17622
  videoSources = {}
17624
17623
  }) => {
17625
17624
  const router = useRouter();
@@ -17768,16 +17767,17 @@ var VideoGridView = React14__default.memo(({
17768
17767
  minHeight: "100%"
17769
17768
  },
17770
17769
  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
- }
17770
+ if (a.line_id !== b.line_id) {
17771
+ return (a.line_id || "").localeCompare(b.line_id || "");
17772
+ }
17773
+ const aMatch = a.workspace_name.match(/WS(\d+)/);
17774
+ const bMatch = b.workspace_name.match(/WS(\d+)/);
17775
+ if (aMatch && bMatch) {
17776
+ const aNum = parseInt(aMatch[1]);
17777
+ const bNum = parseInt(bMatch[1]);
17778
+ return aNum - bNum;
17779
17779
  }
17780
- return aNum - bNum;
17780
+ return a.workspace_name.localeCompare(b.workspace_name);
17781
17781
  }).map((workspace) => {
17782
17782
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
17783
17783
  const isVisible = visibleWorkspaces.has(workspaceId);
@@ -21365,7 +21365,7 @@ function isValidShiftId(shiftId) {
21365
21365
  }
21366
21366
 
21367
21367
  // src/lib/api/s3-clips-parser.ts
21368
- function parseS3Uri(s3Uri) {
21368
+ function parseS3Uri(s3Uri, sopCategories) {
21369
21369
  const path = new URL(s3Uri).pathname;
21370
21370
  const parts = path.split("/").filter((p) => p);
21371
21371
  console.log("S3 URI:", s3Uri);
@@ -21405,6 +21405,27 @@ function parseS3Uri(s3Uri) {
21405
21405
  let description = "Analysis Clip";
21406
21406
  const normalizedViolationType = violationType.toLowerCase().trim();
21407
21407
  console.log(`Parsing violation type: "${violationType}" (normalized: "${normalizedViolationType}") from path: ${s3Uri}`);
21408
+ if (sopCategories && sopCategories.length > 0) {
21409
+ const matchedCategory = sopCategories.find((category) => {
21410
+ const categoryId = category.id.toLowerCase();
21411
+ const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
21412
+ return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
21413
+ normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
21414
+ });
21415
+ if (matchedCategory) {
21416
+ type = matchedCategory.id;
21417
+ description = matchedCategory.description || matchedCategory.label;
21418
+ if (matchedCategory.color.includes("red")) {
21419
+ severity = "high";
21420
+ } else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
21421
+ severity = "medium";
21422
+ } else {
21423
+ severity = "low";
21424
+ }
21425
+ console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
21426
+ return { timestamp, severity, description, type, originalUri: s3Uri };
21427
+ }
21428
+ }
21408
21429
  switch (normalizedViolationType) {
21409
21430
  case "idle_time":
21410
21431
  case "idle":
@@ -21644,11 +21665,23 @@ var S3ClipsService = class {
21644
21665
  const key = url.pathname.startsWith("/") ? url.pathname.substring(1) : url.pathname;
21645
21666
  return `${this.config.s3Config.cloudFrontDomain}/${key}`;
21646
21667
  }
21668
+ /**
21669
+ * Gets SOP categories for a specific workspace
21670
+ */
21671
+ getSOPCategories(workspaceId) {
21672
+ const sopConfig = this.config.s3Config?.sopCategories;
21673
+ if (!sopConfig) return void 0;
21674
+ if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
21675
+ return sopConfig.workspaceOverrides[workspaceId];
21676
+ }
21677
+ return sopConfig.default;
21678
+ }
21647
21679
  /**
21648
21680
  * Processes a single video completely
21649
21681
  */
21650
21682
  async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
21651
- const parsedInfo = parseS3Uri(uri);
21683
+ const sopCategories = this.getSOPCategories(workspaceId);
21684
+ const parsedInfo = parseS3Uri(uri, sopCategories);
21652
21685
  if (!parsedInfo) {
21653
21686
  console.warn(`Skipping URI due to parsing failure: ${uri}`);
21654
21687
  return null;
@@ -21681,27 +21714,32 @@ var S3ClipsService = class {
21681
21714
  async getVideoSummary(workspaceId, date, shiftId) {
21682
21715
  const s3Uris = await this.listS3Clips({ workspaceId, date, shiftId });
21683
21716
  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
- };
21717
+ const sopCategories = this.getSOPCategories(workspaceId);
21718
+ const counts = { total: 0 };
21719
+ const samples = {};
21720
+ if (sopCategories && sopCategories.length > 0) {
21721
+ sopCategories.forEach((category) => {
21722
+ counts[category.id] = 0;
21723
+ samples[category.id] = null;
21724
+ });
21725
+ } else {
21726
+ counts.best_cycle_time = 0;
21727
+ counts.worst_cycle_time = 0;
21728
+ counts.bottleneck = 0;
21729
+ counts.low_value = 0;
21730
+ counts.long_cycle_time = 0;
21731
+ counts.missing_quality_check = 0;
21732
+ counts.cycle_completions = 0;
21733
+ samples.best_cycle_time = null;
21734
+ samples.worst_cycle_time = null;
21735
+ samples.bottleneck = null;
21736
+ samples.low_value = null;
21737
+ samples.long_cycle_time = null;
21738
+ samples.missing_quality_check = null;
21739
+ samples.cycle_completions = null;
21740
+ }
21703
21741
  for (const uri of s3Uris) {
21704
- const parsedInfo = parseS3Uri(uri);
21742
+ const parsedInfo = parseS3Uri(uri, sopCategories);
21705
21743
  if (parsedInfo) {
21706
21744
  const { type } = parsedInfo;
21707
21745
  counts[type] = (counts[type] || 0) + 1;
@@ -21914,6 +21952,14 @@ var BottlenecksContent = ({
21914
21952
  className
21915
21953
  }) => {
21916
21954
  const dashboardConfig = useDashboardConfig();
21955
+ const sopCategories = React14__default.useMemo(() => {
21956
+ const sopConfig = dashboardConfig?.s3Config?.sopCategories;
21957
+ if (!sopConfig) return null;
21958
+ if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
21959
+ return sopConfig.workspaceOverrides[workspaceId];
21960
+ }
21961
+ return sopConfig.default;
21962
+ }, [dashboardConfig, workspaceId]);
21917
21963
  const videoRef = useRef(null);
21918
21964
  const fullscreenContainerRef = useRef(null);
21919
21965
  const timestampFilterRef = useRef(null);
@@ -21922,7 +21968,9 @@ var BottlenecksContent = ({
21922
21968
  const [duration, setDuration] = useState(0);
21923
21969
  const [isFullscreen, setIsFullscreen] = useState(false);
21924
21970
  const [currentIndex, setCurrentIndex] = useState(0);
21925
- const [activeFilter, setActiveFilter] = useState("low_value");
21971
+ const [activeFilter, setActiveFilter] = useState(
21972
+ sopCategories && sopCategories.length > 0 ? sopCategories[0].id : "low_value"
21973
+ );
21926
21974
  const [allVideos, setAllVideos] = useState([]);
21927
21975
  const [isLoading, setIsLoading] = useState(true);
21928
21976
  const [error, setError] = useState(null);
@@ -22034,27 +22082,41 @@ var BottlenecksContent = ({
22034
22082
  let filtered = [];
22035
22083
  if (activeFilter === "all") {
22036
22084
  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
22085
  } else {
22052
- filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22086
+ if (sopCategories && sopCategories.length > 0) {
22087
+ const selectedCategory = sopCategories.find((cat) => cat.id === activeFilter);
22088
+ if (selectedCategory) {
22089
+ filtered = allVideos.filter((video) => video.type === selectedCategory.id);
22090
+ if (selectedCategory.id === "long_cycle_time") {
22091
+ filtered = allVideos.filter(
22092
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22093
+ );
22094
+ }
22095
+ }
22096
+ } else {
22097
+ if (activeFilter === "low_value") {
22098
+ filtered = allVideos.filter((video) => video.type === "low_value");
22099
+ } else if (activeFilter === "sop_deviations") {
22100
+ filtered = allVideos.filter((video) => video.type === "missing_quality_check");
22101
+ } else if (activeFilter === "best_cycle_time") {
22102
+ filtered = allVideos.filter((video) => video.type === "best_cycle_time");
22103
+ } else if (activeFilter === "worst_cycle_time") {
22104
+ filtered = allVideos.filter((video) => video.type === "worst_cycle_time");
22105
+ } else if (activeFilter === "cycle_completions") {
22106
+ filtered = allVideos.filter((video) => video.type === "cycle_completions");
22107
+ } else if (activeFilter === "long_cycle_time") {
22108
+ filtered = allVideos.filter(
22109
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22110
+ );
22111
+ } else {
22112
+ filtered = allVideos.filter((video) => video.type === "bottleneck" && video.severity === activeFilter);
22113
+ }
22114
+ }
22053
22115
  }
22054
22116
  return filtered.sort((a, b) => {
22055
22117
  return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
22056
22118
  });
22057
- }, [activeFilter, allVideos]);
22119
+ }, [activeFilter, allVideos, sopCategories]);
22058
22120
  useEffect(() => {
22059
22121
  if (filteredVideos.length === 0) return;
22060
22122
  const upcoming = [];
@@ -22328,35 +22390,34 @@ var BottlenecksContent = ({
22328
22390
  }
22329
22391
  };
22330
22392
  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(
22393
+ if (!allVideos) return {};
22394
+ const counts = { total: allVideos.length };
22395
+ if (sopCategories && sopCategories.length > 0) {
22396
+ sopCategories.forEach((category) => {
22397
+ if (category.id === "long_cycle_time") {
22398
+ counts[category.id] = allVideos.filter(
22399
+ (video) => video.type === "bottleneck" && video.description.toLowerCase().includes("cycle time")
22400
+ ).length;
22401
+ } else {
22402
+ counts[category.id] = allVideos.filter((video) => video.type === category.id).length;
22403
+ }
22404
+ });
22405
+ } else {
22406
+ counts.bottlenecks = allVideos.filter((video) => video.type === "bottleneck").length;
22407
+ counts.lowValue = allVideos.filter((video) => video.type === "low_value").length;
22408
+ counts.highSeverity = allVideos.filter((video) => video.severity === "high" && video.type === "bottleneck").length;
22409
+ counts.mediumSeverity = allVideos.filter((video) => video.severity === "medium" && video.type === "bottleneck").length;
22410
+ counts.lowSeverity = allVideos.filter((video) => video.severity === "low" && video.type === "bottleneck").length;
22411
+ counts.sopDeviations = allVideos.filter((video) => video.type === "missing_quality_check").length;
22412
+ counts.bestCycleTimes = allVideos.filter((video) => video.type === "best_cycle_time").length;
22413
+ counts.worstCycleTimes = allVideos.filter((video) => video.type === "worst_cycle_time").length;
22414
+ counts.longCycleTimes = allVideos.filter(
22354
22415
  (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]);
22416
+ ).length;
22417
+ counts.cycleCompletions = allVideos.filter((video) => video.type === "cycle_completions").length;
22418
+ }
22419
+ return counts;
22420
+ }, [allVideos, sopCategories]);
22360
22421
  const currentVideo = useMemo(() => {
22361
22422
  if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
22362
22423
  return null;
@@ -22381,6 +22442,19 @@ var BottlenecksContent = ({
22381
22442
  return "Bottleneck";
22382
22443
  }
22383
22444
  };
22445
+ const getColorClasses = (color2) => {
22446
+ const colorMap = {
22447
+ purple: { text: "text-purple-500", bg: "bg-purple-500", dot: "bg-purple-500" },
22448
+ green: { text: "text-green-600", bg: "bg-green-600", dot: "bg-green-600" },
22449
+ red: { text: "text-red-700", bg: "bg-red-700", dot: "bg-red-700" },
22450
+ "red-dark": { text: "text-red-500", bg: "bg-red-500", dot: "bg-red-500" },
22451
+ blue: { text: "text-blue-600", bg: "bg-blue-600", dot: "bg-blue-600" },
22452
+ orange: { text: "text-orange-600", bg: "bg-orange-600", dot: "bg-orange-600" },
22453
+ yellow: { text: "text-yellow-600", bg: "bg-yellow-600", dot: "bg-yellow-600" },
22454
+ gray: { text: "text-gray-600", bg: "bg-gray-600", dot: "bg-gray-600" }
22455
+ };
22456
+ return colorMap[color2] || colorMap.gray;
22457
+ };
22384
22458
  const formatTimeOnly = (time2) => {
22385
22459
  if (!time2) return "";
22386
22460
  try {
@@ -22410,162 +22484,62 @@ var BottlenecksContent = ({
22410
22484
  /* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error })
22411
22485
  ] });
22412
22486
  }
22487
+ const categoriesToShow = sopCategories && sopCategories.length > 0 ? sopCategories : [
22488
+ // Default hardcoded categories if no configuration
22489
+ { id: "low_value", label: "Idle Moments", color: "purple", subtitle: "Idle time detected" },
22490
+ { id: "best_cycle_time", label: "Best Cycle Time", color: "green", subtitle: "Fastest cycle today" },
22491
+ { id: "worst_cycle_time", label: "Worst Cycle Time", color: "red", subtitle: "Slowest cycle today" },
22492
+ { id: "long_cycle_time", label: "Long Cycle Time", color: "red-dark", subtitle: "Above standard cycle times" },
22493
+ { id: "cycle_completions", label: "Cycle Completions", color: "blue", subtitle: "Completed production cycles" }
22494
+ ];
22413
22495
  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(
22496
+ /* @__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) => {
22497
+ const colorClasses = getColorClasses(category.color);
22498
+ const count = clipCounts[category.id] || 0;
22499
+ return /* @__PURE__ */ jsxs(
22476
22500
  Card2,
22477
22501
  {
22478
22502
  onClick: () => {
22479
- setActiveFilter("worst_cycle_time");
22480
- trackCoreEvent("Worst Cycle Time Filter Clicked", {
22503
+ setActiveFilter(category.id);
22504
+ trackCoreEvent(`${category.label} Filter Clicked`, {
22481
22505
  workspaceId,
22482
22506
  workspaceName,
22483
22507
  date,
22484
- filterType: "worst_cycle_time",
22485
- clipCount: clipCounts.worstCycleTimes
22508
+ filterType: category.id,
22509
+ clipCount: count
22486
22510
  });
22487
22511
  },
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)`,
22512
+ 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" : ""}`,
22513
+ "aria-label": `Filter by ${category.label} (${count} clips)`,
22490
22514
  role: "button",
22491
22515
  tabIndex: 0,
22492
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("worst_cycle_time"),
22516
+ onKeyDown: (e) => e.key === "Enter" && setActiveFilter(category.id),
22493
22517
  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" }) }),
22518
+ /* @__PURE__ */ jsx(CardHeader2, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle2, { className: `text-lg ${activeFilter === category.id ? "text-blue-600" : ""}`, children: category.label }) }),
22495
22519
  /* @__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 }),
22520
+ /* @__PURE__ */ jsx("p", { className: `text-3xl font-bold ${colorClasses.text}`, children: count }),
22497
22521
  /* @__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" })
22522
+ /* @__PURE__ */ jsx("div", { className: `h-2 w-2 rounded-full ${colorClasses.dot} mr-1.5` }),
22523
+ /* @__PURE__ */ jsx("span", { children: category.subtitle || category.description || category.label })
22500
22524
  ] })
22501
22525
  ] }) })
22502
22526
  ]
22503
- }
22504
- ),
22505
- /* @__PURE__ */ jsxs(
22506
- Card2,
22507
- {
22508
- onClick: () => {
22509
- setActiveFilter("long_cycle_time");
22510
- trackCoreEvent("Long Cycle Time Filter Clicked", {
22511
- workspaceId,
22512
- workspaceName,
22513
- date,
22514
- filterType: "long_cycle_time",
22515
- clipCount: clipCounts.longCycleTimes
22516
- });
22517
- },
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)`,
22520
- role: "button",
22521
- tabIndex: 0,
22522
- onKeyDown: (e) => e.key === "Enter" && setActiveFilter("long_cycle_time"),
22523
- 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" }) }),
22525
- /* @__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 }),
22527
- /* @__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" })
22530
- ] })
22531
- ] }) })
22532
- ]
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
- ] }),
22527
+ },
22528
+ category.id
22529
+ );
22530
+ }) }),
22566
22531
  /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden", style: { height: "calc(100% - 8.5rem)" }, children: [
22567
22532
  /* @__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})` }),
22533
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-800", children: (() => {
22534
+ if (activeFilter === "all") {
22535
+ return `All Clips (${clipCounts.total || 0})`;
22536
+ }
22537
+ const activeCategory = categoriesToShow.find((cat) => cat.id === activeFilter);
22538
+ if (activeCategory) {
22539
+ return `${activeCategory.label} (${clipCounts[activeCategory.id] || 0})`;
22540
+ }
22541
+ 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})`;
22542
+ })() }),
22569
22543
  /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
22570
22544
  /* @__PURE__ */ jsxs("div", { className: "relative", ref: timestampFilterRef, children: [
22571
22545
  /* @__PURE__ */ jsx(
@@ -24941,14 +24915,6 @@ var AIAgentView = () => {
24941
24915
  return newMap;
24942
24916
  });
24943
24917
  },
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
24918
  onComplete: async (messageId) => {
24953
24919
  if (currentThreadId && !currentThreadId.startsWith("temp-")) {
24954
24920
  const updatedMessages = await getAllThreadMessages(currentThreadId);
@@ -27337,14 +27303,20 @@ function HomeView({
27337
27303
  }) {
27338
27304
  const [isHydrated, setIsHydrated] = useState(false);
27339
27305
  const availableLineIds = useMemo(() => [factoryViewId, ...allLineIds], [factoryViewId, allLineIds]);
27340
- const [selectedLineId, setSelectedLineId] = useState(defaultLineId);
27306
+ const [selectedLineId, setSelectedLineId] = useState(factoryViewId);
27341
27307
  const [isChangingFilter, setIsChangingFilter] = useState(false);
27342
27308
  const [errorMessage, setErrorMessage] = useState(null);
27343
27309
  const [displayNamesInitialized, setDisplayNamesInitialized] = useState(false);
27344
27310
  useEffect(() => {
27345
27311
  const initDisplayNames = async () => {
27346
27312
  try {
27347
- await preInitializeWorkspaceDisplayNames(selectedLineId);
27313
+ if (selectedLineId === factoryViewId) {
27314
+ for (const lineId of allLineIds) {
27315
+ await preInitializeWorkspaceDisplayNames(lineId);
27316
+ }
27317
+ } else {
27318
+ await preInitializeWorkspaceDisplayNames(selectedLineId);
27319
+ }
27348
27320
  setDisplayNamesInitialized(true);
27349
27321
  } catch (error) {
27350
27322
  console.error("Failed to pre-initialize workspace display names:", error);
@@ -27352,7 +27324,7 @@ function HomeView({
27352
27324
  }
27353
27325
  };
27354
27326
  initDisplayNames();
27355
- }, [selectedLineId]);
27327
+ }, [selectedLineId, factoryViewId, allLineIds]);
27356
27328
  const {
27357
27329
  displayNames: workspaceDisplayNames,
27358
27330
  loading: displayNamesLoading,
@@ -27432,10 +27404,15 @@ function HomeView({
27432
27404
  const lineTitle = useMemo(() => {
27433
27405
  return factoryName;
27434
27406
  }, [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]);
27407
+ const lineSelectorComponent = useMemo(() => {
27408
+ if (allLineIds.length <= 1) {
27409
+ return null;
27410
+ }
27411
+ return /* @__PURE__ */ jsxs(Select, { onValueChange: handleLineChange, defaultValue: selectedLineId, children: [
27412
+ /* @__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" }) }),
27413
+ /* @__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)) })
27414
+ ] });
27415
+ }, [availableLineIds, handleLineChange, selectedLineId, lineNames, factoryViewId, allLineIds.length]);
27439
27416
  const isLoading = !isHydrated || metricsLoading || kpisLoading || isChangingFilter || displayNamesLoading || !displayNamesInitialized;
27440
27417
  if (isLoading) {
27441
27418
  return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-slate-50", children: /* @__PURE__ */ jsx(LoadingPageCmp, { message: "Loading dashboard..." }) });
@@ -27471,7 +27448,7 @@ function HomeView({
27471
27448
  }
27472
27449
  ) }) }),
27473
27450
  /* @__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 }),
27451
+ lineSelectorComponent && /* @__PURE__ */ jsx("div", { className: "absolute right-3 top-2 sm:right-6 sm:top-3 z-30", children: lineSelectorComponent }),
27475
27452
  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
27453
  workspaces: memoizedWorkspaceMetrics,
27477
27454
  lineNames,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.0.0",
3
+ "version": "6.0.1",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",