@optifye/dashboard-core 6.11.3 → 6.11.5

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.css CHANGED
@@ -1173,12 +1173,12 @@ body {
1173
1173
  .max-h-\[300px\] {
1174
1174
  max-height: 300px;
1175
1175
  }
1176
- .max-h-\[380px\] {
1177
- max-height: 380px;
1178
- }
1179
1176
  .max-h-\[400px\] {
1180
1177
  max-height: 400px;
1181
1178
  }
1179
+ .max-h-\[500px\] {
1180
+ max-height: 500px;
1181
+ }
1182
1182
  .max-h-\[60vh\] {
1183
1183
  max-height: 60vh;
1184
1184
  }
@@ -1188,6 +1188,9 @@ body {
1188
1188
  .max-h-\[90vh\] {
1189
1189
  max-height: 90vh;
1190
1190
  }
1191
+ .max-h-\[calc\(100vh-80px\)\] {
1192
+ max-height: calc(100vh - 80px);
1193
+ }
1191
1194
  .max-h-full {
1192
1195
  max-height: 100%;
1193
1196
  }
@@ -1443,6 +1446,9 @@ body {
1443
1446
  .min-w-\[250px\] {
1444
1447
  min-width: 250px;
1445
1448
  }
1449
+ .min-w-\[280px\] {
1450
+ min-width: 280px;
1451
+ }
1446
1452
  .min-w-\[32px\] {
1447
1453
  min-width: 32px;
1448
1454
  }
@@ -1494,6 +1500,9 @@ body {
1494
1500
  .max-w-\[150px\] {
1495
1501
  max-width: 150px;
1496
1502
  }
1503
+ .max-w-\[170px\] {
1504
+ max-width: 170px;
1505
+ }
1497
1506
  .max-w-\[1800px\] {
1498
1507
  max-width: 1800px;
1499
1508
  }
@@ -2957,6 +2966,9 @@ body {
2957
2966
  .bg-slate-50\/50 {
2958
2967
  background-color: rgb(248 250 252 / 0.5);
2959
2968
  }
2969
+ .bg-slate-50\/80 {
2970
+ background-color: rgb(248 250 252 / 0.8);
2971
+ }
2960
2972
  .bg-slate-900 {
2961
2973
  --tw-bg-opacity: 1;
2962
2974
  background-color: rgb(15 23 42 / var(--tw-bg-opacity, 1));
package/dist/index.d.mts CHANGED
@@ -7057,6 +7057,7 @@ interface LineChartProps {
7057
7057
  yAxisUnit?: string;
7058
7058
  yAxisDomain?: [number | string, number | string];
7059
7059
  tooltipFormatter?: (value: any, name: any, props: any) => any;
7060
+ tooltipLabelFormatter?: (label: any, payload: any[]) => any;
7060
7061
  legendPayload?: any[];
7061
7062
  className?: string;
7062
7063
  showGrid?: boolean;
package/dist/index.d.ts CHANGED
@@ -7057,6 +7057,7 @@ interface LineChartProps {
7057
7057
  yAxisUnit?: string;
7058
7058
  yAxisDomain?: [number | string, number | string];
7059
7059
  tooltipFormatter?: (value: any, name: any, props: any) => any;
7060
+ tooltipLabelFormatter?: (label: any, payload: any[]) => any;
7060
7061
  legendPayload?: any[];
7061
7062
  className?: string;
7062
7063
  showGrid?: boolean;
package/dist/index.js CHANGED
@@ -29797,6 +29797,7 @@ var LineChartComponent = ({
29797
29797
  yAxisUnit,
29798
29798
  yAxisDomain,
29799
29799
  tooltipFormatter,
29800
+ tooltipLabelFormatter,
29800
29801
  legendPayload,
29801
29802
  className,
29802
29803
  showGrid = true,
@@ -29876,7 +29877,15 @@ var LineChartComponent = ({
29876
29877
  stroke: axisStrokeColor
29877
29878
  }
29878
29879
  ),
29879
- showTooltip && /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { formatter: tooltipFormatter || defaultTooltipFormatter, itemStyle: { color: "#111827" }, cursor: { strokeDasharray: "3 3" } }),
29880
+ showTooltip && /* @__PURE__ */ jsxRuntime.jsx(
29881
+ recharts.Tooltip,
29882
+ {
29883
+ formatter: tooltipFormatter || defaultTooltipFormatter,
29884
+ labelFormatter: tooltipLabelFormatter,
29885
+ itemStyle: { color: "#111827" },
29886
+ cursor: { strokeDasharray: "3 3" }
29887
+ }
29888
+ ),
29880
29889
  showLegend && /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { payload: legendPayload }),
29881
29890
  lines.map((lineConfig, index) => {
29882
29891
  const lineProps = {
@@ -29932,7 +29941,7 @@ var LineChart = React141__namespace.default.memo(LineChartComponent, (prevProps,
29932
29941
  })) {
29933
29942
  return false;
29934
29943
  }
29935
- if (prevProps.tooltipFormatter !== nextProps.tooltipFormatter || prevProps.xAxisTickFormatter !== nextProps.xAxisTickFormatter || JSON.stringify(prevProps.legendPayload) !== JSON.stringify(nextProps.legendPayload)) {
29944
+ if (prevProps.tooltipFormatter !== nextProps.tooltipFormatter || prevProps.xAxisTickFormatter !== nextProps.xAxisTickFormatter || prevProps.tooltipLabelFormatter !== nextProps.tooltipLabelFormatter || JSON.stringify(prevProps.legendPayload) !== JSON.stringify(nextProps.legendPayload)) {
29936
29945
  return false;
29937
29946
  }
29938
29947
  return true;
@@ -42647,22 +42656,16 @@ var formatDuration = (seconds) => {
42647
42656
  const hours = Math.floor(wholeSeconds % 86400 / 3600);
42648
42657
  const minutes = Math.floor(wholeSeconds % 3600 / 60);
42649
42658
  const remainingSeconds = wholeSeconds % 60;
42650
- if (days > 0) {
42651
- return `${days}d ${hours}h ${minutes}m ${remainingSeconds}s`;
42652
- }
42653
- if (hours > 0) {
42654
- return `${hours}h ${minutes}m ${remainingSeconds}s`;
42655
- }
42656
- if (minutes > 0) {
42657
- return `${minutes}m ${remainingSeconds}s`;
42658
- }
42659
- return `${remainingSeconds}s`;
42660
- };
42661
- var formatPercentage = (value) => {
42662
- if (value === null || value === void 0 || !Number.isFinite(value)) {
42663
- return "--";
42659
+ const parts = [];
42660
+ if (days > 0) parts.push(`${days}d`);
42661
+ if (hours > 0) parts.push(`${hours}h`);
42662
+ if (minutes > 0) parts.push(`${minutes}m`);
42663
+ if (days === 0 && hours === 0) {
42664
+ if (remainingSeconds > 0 || parts.length === 0) {
42665
+ parts.push(`${remainingSeconds}s`);
42666
+ }
42664
42667
  }
42665
- return `${value.toFixed(1)}%`;
42668
+ return parts.join(" ");
42666
42669
  };
42667
42670
  var getColorForEntry = (name, index) => {
42668
42671
  const normalized = name.trim().toLowerCase();
@@ -42682,30 +42685,35 @@ var CustomTooltip = ({ active, payload, hideTotalDuration }) => {
42682
42685
  if (active && payload && payload.length) {
42683
42686
  const datum = payload[0]?.payload;
42684
42687
  const contributors = datum?.contributors || [];
42685
- const fill = payload[0]?.payload?.fill || payload[0]?.fill || getColorForEntry(payload[0].name, 0);
42686
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 border border-gray-200 shadow-xl rounded-lg z-50 min-w-[200px]", children: [
42687
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 mb-2 pb-1 border-b border-gray-100", children: [
42688
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2.5 h-2.5 rounded-full flex-shrink-0", style: { backgroundColor: fill } }),
42689
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-slate-800 text-sm", children: payload[0].name.replace(/_/g, " ") })
42688
+ let effContributionStr = "--";
42689
+ if (datum?.efficiencyLossPercentage !== null && datum?.efficiencyLossPercentage !== void 0 && Number.isFinite(datum?.efficiencyLossPercentage)) {
42690
+ const val = datum.efficiencyLossPercentage;
42691
+ effContributionStr = val === 0 ? "0.0%" : `${Math.abs(val).toFixed(1)}%`;
42692
+ }
42693
+ const hasContributors = contributors.length > 0;
42694
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white p-3 border border-gray-100 shadow-lg rounded-lg text-xs z-50 min-w-[280px]", children: [
42695
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 pb-2 mb-2 border-b border-slate-100", children: [
42696
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2.5 h-2.5 rounded-full flex-shrink-0", style: { backgroundColor: payload[0].payload.fill || getColorForEntry(payload[0].name, 0) } }),
42697
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-slate-800 text-[13px]", children: payload[0].name.replace(/_/g, " ") })
42690
42698
  ] }),
42691
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 text-xs", children: [
42692
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center gap-3", children: [
42699
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5 pb-1", children: [
42700
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center gap-4", children: [
42693
42701
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-500", children: "Share of idle time" }),
42694
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-800", children: `${payload[0].value.toFixed(1)}%` })
42702
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-slate-800", children: `${payload[0].value.toFixed(1)}%` })
42695
42703
  ] }),
42696
- !hideTotalDuration && datum?.totalDurationSeconds !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center gap-3", children: [
42704
+ !hideTotalDuration && !hasContributors && datum?.totalDurationSeconds !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center gap-4", children: [
42697
42705
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-500", children: "Total Duration" }),
42698
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-800", children: formatDuration(datum.totalDurationSeconds) })
42706
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-slate-800", children: formatDuration(datum.totalDurationSeconds) })
42699
42707
  ] }) : null,
42700
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center gap-3", children: [
42708
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center gap-4", children: [
42701
42709
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-500", children: "Efficiency Loss" }),
42702
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-slate-800", children: formatPercentage(datum?.efficiencyLossPercentage) })
42710
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-slate-800", children: effContributionStr })
42703
42711
  ] })
42704
42712
  ] }),
42705
- contributors.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 pt-2 border-t border-gray-100", children: [
42706
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[9px] font-semibold uppercase tracking-wider text-slate-400 mb-1.5", children: "Top Workstations" }),
42707
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1", children: contributors.slice(0, 3).map((contributor) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2 text-[10px]", children: [
42708
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-700 truncate max-w-[120px]", children: contributor.workspaceName }),
42713
+ hasContributors ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 pt-2 border-t border-slate-100", children: [
42714
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-2", children: "TOP CONTRIBUTORS" }),
42715
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: contributors.slice(0, 3).map((contributor) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
42716
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-600 truncate max-w-[170px]", title: contributor.workspaceName, children: contributor.workspaceName }),
42709
42717
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-slate-500 text-right whitespace-nowrap", children: `${formatDuration(contributor.totalDurationSeconds)} \u2022 ${Number(contributor.percentageWithinReason || 0).toFixed(1)}%` })
42710
42718
  ] }, `${payload[0].name}-${contributor.workspaceId}`)) })
42711
42719
  ] }) : null
@@ -49386,10 +49394,11 @@ var AlertsPopup = ({
49386
49394
  const updatePosition = React141.useCallback(() => {
49387
49395
  const viewportHeight = window.innerHeight;
49388
49396
  const viewportWidth = window.innerWidth;
49397
+ const currentPopupHeight = popupRef.current?.offsetHeight || 280;
49389
49398
  if (viewportWidth < 768) {
49390
- const mobileWidth = Math.max(240, Math.min(360, viewportWidth - 32));
49399
+ const mobileWidth = Math.max(240, Math.min(380, viewportWidth - 32));
49391
49400
  setPosition({
49392
- top: Math.max(16, (viewportHeight - 280) / 2),
49401
+ top: Math.max(16, (viewportHeight - currentPopupHeight) / 2),
49393
49402
  left: Math.max(16, (viewportWidth - mobileWidth) / 2),
49394
49403
  width: mobileWidth
49395
49404
  });
@@ -49397,22 +49406,25 @@ var AlertsPopup = ({
49397
49406
  }
49398
49407
  if (!triggerRef.current) return;
49399
49408
  const rect = triggerRef.current.getBoundingClientRect();
49400
- const popupHeight = 280;
49401
- let top = rect.bottom - popupHeight + 40;
49402
- if (top < 16) top = 16;
49403
- if (top + popupHeight > viewportHeight - 16) {
49404
- top = viewportHeight - popupHeight - 16;
49405
- }
49409
+ const bottomOffset = viewportHeight - rect.bottom + 12;
49406
49410
  setPosition({
49407
- top,
49411
+ bottom: bottomOffset,
49408
49412
  left: 88,
49409
49413
  // Sidebar width (80px) + gap (8px)
49410
- width: 360
49414
+ width: 400
49415
+ // Make it a bit wider for enterprise/cleaner look
49411
49416
  });
49412
49417
  }, [triggerRef]);
49413
49418
  React141.useEffect(() => {
49414
49419
  if (isOpen) {
49415
- updatePosition();
49420
+ const timer = setTimeout(() => {
49421
+ updatePosition();
49422
+ }, 0);
49423
+ return () => clearTimeout(timer);
49424
+ }
49425
+ }, [isOpen, alerts, isLoading, error, updatePosition]);
49426
+ React141.useEffect(() => {
49427
+ if (isOpen) {
49416
49428
  window.addEventListener("resize", updatePosition);
49417
49429
  window.addEventListener("scroll", updatePosition);
49418
49430
  }
@@ -49458,19 +49470,21 @@ var AlertsPopup = ({
49458
49470
  },
49459
49471
  style: {
49460
49472
  position: "fixed",
49461
- top: position.top,
49473
+ ...position.top !== void 0 ? { top: position.top } : {},
49474
+ ...position.bottom !== void 0 ? { bottom: position.bottom } : {},
49462
49475
  left: position.left,
49463
49476
  width: position.width,
49477
+ maxHeight: "calc(100vh - 32px)",
49464
49478
  zIndex: 2147483647
49465
49479
  },
49466
- className: "bg-white rounded-[16px] shadow-xl border border-gray-100 max-w-[calc(100vw-32px)] overflow-hidden flex flex-col",
49480
+ className: "bg-white rounded-[16px] shadow-2xl border border-gray-100 max-w-[calc(100vw-32px)] max-h-[calc(100vh-80px)] overflow-hidden flex flex-col",
49467
49481
  children: [
49468
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-gray-50 bg-[#fafafa]", children: [
49482
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-gray-50 bg-[#fafafa] shrink-0", children: [
49469
49483
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
49470
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[14px] font-bold text-[#111827] uppercase tracking-wide", children: "Recent Alerts" }),
49484
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[14px] font-bold text-[#111827] uppercase tracking-wide", children: "Current Alerts" }),
49471
49485
  alertsCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "bg-[#eff6ff] text-[#2563eb] text-[11px] font-bold px-2 py-0.5 rounded-full", children: [
49472
49486
  alertsCount,
49473
- " Active"
49487
+ " alerts"
49474
49488
  ] })
49475
49489
  ] }),
49476
49490
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -49482,13 +49496,13 @@ var AlertsPopup = ({
49482
49496
  }
49483
49497
  )
49484
49498
  ] }),
49485
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col max-h-[380px] overflow-y-auto", children: [
49486
- isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-10 text-sm text-gray-500", children: "Loading current shift alerts..." }),
49499
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col flex-1 min-h-0 overflow-y-auto max-h-[500px]", children: [
49500
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-10 text-sm text-gray-500", children: "Loading alerts..." }),
49487
49501
  !isLoading && error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-10 text-sm text-red-600", children: error }),
49488
- !isLoading && !error && alerts.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-10 text-sm text-gray-500", children: "No active alerts for the current shift." }),
49502
+ !isLoading && !error && alerts.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-10 text-sm text-gray-500", children: "No alerts right now." }),
49489
49503
  !isLoading && !error && alerts.map((alert2, index) => {
49490
49504
  const isCritical = typeof alert2.delta_efficiency === "number" && alert2.delta_efficiency <= -10;
49491
- const description = `${alert2.monitoring_mode === "uptime" ? "Uptime" : "Output"} efficiency is ${formatPercent(alert2.current_efficiency)} vs last week's ${formatPercent(alert2.baseline_efficiency)} average.`;
49505
+ const description = `Now: ${formatPercent(alert2.current_efficiency)}. Usual for this shift: ${formatPercent(alert2.baseline_efficiency)}.`;
49492
49506
  return /* @__PURE__ */ jsxRuntime.jsx(
49493
49507
  motion.div,
49494
49508
  {
@@ -49505,20 +49519,13 @@ var AlertsPopup = ({
49505
49519
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
49506
49520
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-start mb-1.5", children: [
49507
49521
  /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold text-[#111827]", children: alert2.line_name }),
49508
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[12px] text-[#94a3b8] font-medium whitespace-nowrap ml-3", children: formatRelativeTime2(alert2.evaluated_at) })
49509
- ] }),
49510
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[12px] font-medium text-[#475569] uppercase tracking-wide", children: [
49511
- alert2.monitoring_mode === "uptime" ? "Uptime" : "Output",
49512
- " regression"
49522
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[12px] text-[#94a3b8] font-medium whitespace-nowrap ml-3", children: [
49523
+ "Checked ",
49524
+ formatRelativeTime2(alert2.evaluated_at)
49525
+ ] })
49513
49526
  ] }),
49514
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[13.5px] text-[#64748b] leading-[1.45] mt-1", children: description }),
49515
- /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[12px] text-[#94a3b8] mt-2", children: [
49516
- "Delta ",
49517
- formatPercent(alert2.delta_efficiency),
49518
- " across ",
49519
- alert2.baseline_sample_size,
49520
- " historical shifts"
49521
- ] })
49527
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[12px] font-medium text-[#475569] uppercase tracking-wide", children: alert2.monitoring_mode === "uptime" ? "Machine Utilization is low" : "Output is low" }),
49528
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[13.5px] text-[#64748b] leading-[1.45] mt-1", children: description })
49522
49529
  ] })
49523
49530
  ] })
49524
49531
  },
@@ -49581,7 +49588,7 @@ var SideNavBar = React141.memo(({
49581
49588
  const handleHomeClick = React141.useCallback(() => {
49582
49589
  navigate("/", {
49583
49590
  trackingEvent: {
49584
- name: "Line Overview Page Clicked",
49591
+ name: "Operations Overview Page Clicked",
49585
49592
  properties: {
49586
49593
  source: "side_nav",
49587
49594
  line_id: lineId,
@@ -49869,6 +49876,7 @@ var SideNavBar = React141.memo(({
49869
49876
  }
49870
49877
  }, [signOut]);
49871
49878
  const handleLogoClick = React141.useCallback(() => {
49879
+ trackCoreEvent("Operations Overview Page Clicked", { source: "logo" });
49872
49880
  navigate("/");
49873
49881
  onMobileMenuClose?.();
49874
49882
  }, [navigate, onMobileMenuClose]);
@@ -50021,9 +50029,11 @@ var SideNavBar = React141.memo(({
50021
50029
  {
50022
50030
  ref: alertsTriggerRef,
50023
50031
  onClick: () => {
50024
- setIsAlertsOpen(!isAlertsOpen);
50032
+ const willOpen = !isAlertsOpen;
50033
+ setIsAlertsOpen(willOpen);
50025
50034
  setIsSettingsOpen(false);
50026
- if (!isAlertsOpen) {
50035
+ if (willOpen) {
50036
+ trackCoreEvent("Alerts Page Clicked", { source: "side_nav" });
50027
50037
  void refreshAlertsSummary();
50028
50038
  }
50029
50039
  },
@@ -50181,6 +50191,7 @@ var SideNavBar = React141.memo(({
50181
50191
  {
50182
50192
  onClick: () => {
50183
50193
  setIsAlertsOpen(true);
50194
+ trackCoreEvent("Alerts Page Clicked", { source: "side_nav_mobile" });
50184
50195
  void refreshAlertsSummary();
50185
50196
  onMobileMenuClose?.();
50186
50197
  },
@@ -59990,6 +60001,7 @@ var KPIsOverviewView = ({
59990
60001
  backLinkUrl,
59991
60002
  lineIds
59992
60003
  }) => {
60004
+ const router$1 = router.useRouter();
59993
60005
  const [lines, setLines] = React141.useState([]);
59994
60006
  const [leaderboardLines, setLeaderboardLines] = React141.useState([]);
59995
60007
  const [leaderboardLinesLoading, setLeaderboardLinesLoading] = React141.useState(false);
@@ -60017,6 +60029,13 @@ var KPIsOverviewView = ({
60017
60029
  const [monthlyError, setMonthlyError] = React141.useState(null);
60018
60030
  const dailyRequestKeyRef = React141.useRef(null);
60019
60031
  const monthlyRequestKeyRef = React141.useRef(null);
60032
+ React141.useEffect(() => {
60033
+ if (!router$1.isReady) return;
60034
+ const tab = router$1.query.tab;
60035
+ if (tab === "leaderboard") {
60036
+ setActiveTab("leaderboard");
60037
+ }
60038
+ }, [router$1.isReady, router$1.query.tab]);
60020
60039
  const supabase = useSupabase();
60021
60040
  const { user } = useAuth();
60022
60041
  const dashboardConfig = useDashboardConfig();
@@ -72285,13 +72304,7 @@ var formatIdleDuration = (seconds) => {
72285
72304
  if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
72286
72305
  return "--";
72287
72306
  }
72288
- const wholeSeconds = Math.max(0, Math.round(seconds));
72289
- const minutes = Math.floor(wholeSeconds / 60);
72290
- const remainingSeconds = wholeSeconds % 60;
72291
- if (minutes > 0) {
72292
- return `${minutes}m ${remainingSeconds}s`;
72293
- }
72294
- return `${remainingSeconds}s`;
72307
+ return formatIdleTime(Math.max(0, Math.round(seconds)));
72295
72308
  };
72296
72309
  var formatSignedIdleDuration = (seconds) => {
72297
72310
  const sign = seconds > 0 ? "+" : "-";
@@ -72418,6 +72431,7 @@ var PlantHeadView = () => {
72418
72431
  const [trendFetchNonce, setTrendFetchNonce] = React141__namespace.default.useState(0);
72419
72432
  const [trendViewOpenNonce] = React141__namespace.default.useState(() => Date.now());
72420
72433
  const [idleReasonBreakdown, setIdleReasonBreakdown] = React141__namespace.default.useState([]);
72434
+ const [idleReasonScope, setIdleReasonScope] = React141__namespace.default.useState(null);
72421
72435
  const [isIdleReasonBreakdownLoading, setIsIdleReasonBreakdownLoading] = React141__namespace.default.useState(false);
72422
72436
  const [improvements, setImprovements] = React141__namespace.default.useState([]);
72423
72437
  const [isImprovementsLoading, setIsImprovementsLoading] = React141__namespace.default.useState(false);
@@ -72425,6 +72439,35 @@ var PlantHeadView = () => {
72425
72439
  const filterRef = React141__namespace.default.useRef(null);
72426
72440
  const filterButtonRef = React141__namespace.default.useRef(null);
72427
72441
  const mobileFilterButtonRef = React141__namespace.default.useRef(null);
72442
+ React141__namespace.default.useEffect(() => {
72443
+ trackCorePageView("Operations Overview");
72444
+ }, []);
72445
+ const handleDateRangeChange = React141__namespace.default.useCallback((range) => {
72446
+ trackCoreEvent("Operations Overview Date Range Changed", {
72447
+ start_date: range.startKey,
72448
+ end_date: range.endKey
72449
+ });
72450
+ setDateRange(range);
72451
+ }, []);
72452
+ const handleFilterToggle = React141__namespace.default.useCallback(() => {
72453
+ trackCoreEvent("Operations Overview Filter Toggled", {
72454
+ action: !isFilterOpen ? "open" : "close"
72455
+ });
72456
+ setIsFilterOpen(!isFilterOpen);
72457
+ }, [isFilterOpen]);
72458
+ const handleTrendModeChange = React141__namespace.default.useCallback((e) => {
72459
+ const newMode = e.target.value;
72460
+ trackCoreEvent("Operations Overview Shift Filter Changed", {
72461
+ shift_mode: newMode
72462
+ });
72463
+ setTrendMode(newMode);
72464
+ }, []);
72465
+ const handlePoorestLineModeChange = React141__namespace.default.useCallback((mode) => {
72466
+ trackCoreEvent("Operations Overview Poorest Line Mode Changed", {
72467
+ mode
72468
+ });
72469
+ setPoorestLineMode(mode);
72470
+ }, []);
72428
72471
  React141__namespace.default.useEffect(() => {
72429
72472
  function handleClickOutside(event) {
72430
72473
  if (filterRef.current && !filterRef.current.contains(event.target) && filterButtonRef.current && !filterButtonRef.current.contains(event.target) && mobileFilterButtonRef.current && !mobileFilterButtonRef.current.contains(event.target)) {
@@ -72574,11 +72617,13 @@ var PlantHeadView = () => {
72574
72617
  React141__namespace.default.useEffect(() => {
72575
72618
  if (!supabase || !companyId) return void 0;
72576
72619
  if (scopedLineIds.length === 0) {
72620
+ setIdleReasonScope(null);
72577
72621
  setIdleReasonBreakdown([]);
72578
72622
  setIsIdleReasonBreakdownLoading(false);
72579
72623
  return void 0;
72580
72624
  }
72581
72625
  let cancelled = false;
72626
+ setIdleReasonScope(null);
72582
72627
  setIdleReasonBreakdown([]);
72583
72628
  setIsIdleReasonBreakdownLoading(true);
72584
72629
  const fetchIdleReasonBreakdown = async () => {
@@ -72594,12 +72639,14 @@ var PlantHeadView = () => {
72594
72639
  `/api/dashboard/operations-overview/idle-reason-breakdown?${params.toString()}`
72595
72640
  );
72596
72641
  if (!cancelled) {
72642
+ setIdleReasonScope(response.scope || null);
72597
72643
  setIdleReasonBreakdown(response.idle_reason_breakdown || []);
72598
72644
  }
72599
72645
  };
72600
72646
  fetchIdleReasonBreakdown().catch((error) => {
72601
72647
  console.error("[PlantHeadView] Failed to fetch idle reason breakdown", error);
72602
72648
  if (!cancelled) {
72649
+ setIdleReasonScope(null);
72603
72650
  setIdleReasonBreakdown([]);
72604
72651
  }
72605
72652
  }).finally(() => {
@@ -72695,13 +72742,17 @@ var PlantHeadView = () => {
72695
72742
  });
72696
72743
  }, [comparisonLabel, overview.summary?.avg_idle_per_workstation?.delta_seconds]);
72697
72744
  const trendData = React141__namespace.default.useMemo(() => {
72698
- return (overview.trend?.points || []).map((point) => ({
72699
- name: point.date ? dateFns.format(parseDateKeyToDate(point.date), "MMM d") : "",
72700
- efficiency: (() => {
72701
- const value = toNumber3(point.avg_efficiency);
72702
- return value === null ? void 0 : value;
72703
- })()
72704
- }));
72745
+ return (overview.trend?.points || []).map((point) => {
72746
+ const pointDate = point.date ? parseDateKeyToDate(point.date) : null;
72747
+ return {
72748
+ name: pointDate ? dateFns.format(pointDate, "MMM d") : "",
72749
+ dayOfWeek: pointDate ? dateFns.format(pointDate, "EEEE") : "",
72750
+ efficiency: (() => {
72751
+ const value = toNumber3(point.avg_efficiency);
72752
+ return value === null ? void 0 : value;
72753
+ })()
72754
+ };
72755
+ });
72705
72756
  }, [overview.trend?.points]);
72706
72757
  const trendPlayKey = React141__namespace.default.useMemo(() => {
72707
72758
  return `${trendViewOpenNonce}:${trendFetchNonce}`;
@@ -72720,6 +72771,10 @@ var PlantHeadView = () => {
72720
72771
  }))
72721
72772
  })).filter((item) => item.value > 0);
72722
72773
  }, [idleReasonBreakdown]);
72774
+ const showIdleModuleNotEnabledState = React141__namespace.default.useMemo(() => {
72775
+ const enabledLineCount = idleReasonScope?.idle_time_vlm_enabled_line_count;
72776
+ return !isIdleReasonBreakdownLoading && scopedLineIds.length > 0 && typeof enabledLineCount === "number" && enabledLineCount === 0;
72777
+ }, [idleReasonScope?.idle_time_vlm_enabled_line_count, isIdleReasonBreakdownLoading, scopedLineIds.length]);
72723
72778
  const mergedPoorestLines = React141__namespace.default.useMemo(() => {
72724
72779
  const rows = overview.poorest_lines?.[poorestLineMode] || [];
72725
72780
  return rows.slice(0, 3).map((line) => {
@@ -72740,6 +72795,11 @@ var PlantHeadView = () => {
72740
72795
  const showPoorestModeToggle = !!availableLineModes?.has_output && !!availableLineModes?.has_uptime;
72741
72796
  availableLineModes?.has_uptime && !availableLineModes?.has_output ? "Uptime" : "Output";
72742
72797
  const poorestMetricLabel = poorestLineMode === "uptime" ? "Uptime" : "Efficiency";
72798
+ const trendTooltipLabelFormatter = React141__namespace.default.useCallback((label, payload) => {
72799
+ const dayOfWeek = payload?.[0]?.payload?.dayOfWeek;
72800
+ if (!dayOfWeek || typeof label !== "string") return label;
72801
+ return `${label} (${dayOfWeek})`;
72802
+ }, []);
72743
72803
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col min-h-screen bg-slate-50 w-full font-sans", children: [
72744
72804
  /* @__PURE__ */ jsxRuntime.jsx("header", { className: "sticky top-0 z-10 bg-white border-b flex-shrink-0 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-4 md:px-6 py-2 sm:py-3 relative", children: [
72745
72805
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
@@ -72766,7 +72826,7 @@ var PlantHeadView = () => {
72766
72826
  year: parseDateKeyToDate(dateRange.startKey).getFullYear(),
72767
72827
  timezone: appTimezone,
72768
72828
  value: dateRange,
72769
- onChange: setDateRange,
72829
+ onChange: handleDateRangeChange,
72770
72830
  showLabel: false
72771
72831
  }
72772
72832
  ),
@@ -72774,7 +72834,7 @@ var PlantHeadView = () => {
72774
72834
  "button",
72775
72835
  {
72776
72836
  ref: mobileFilterButtonRef,
72777
- onClick: () => setIsFilterOpen(!isFilterOpen),
72837
+ onClick: handleFilterToggle,
72778
72838
  className: `p-2 rounded-full transition-colors relative ${isFilterOpen || trendMode !== "all" ? "bg-blue-50" : "active:bg-gray-100"}`,
72779
72839
  "aria-label": "Open filters",
72780
72840
  children: [
@@ -72802,7 +72862,7 @@ var PlantHeadView = () => {
72802
72862
  year: parseDateKeyToDate(dateRange.startKey).getFullYear(),
72803
72863
  timezone: appTimezone,
72804
72864
  value: dateRange,
72805
- onChange: setDateRange,
72865
+ onChange: handleDateRangeChange,
72806
72866
  showLabel: false
72807
72867
  }
72808
72868
  ) }),
@@ -72810,7 +72870,7 @@ var PlantHeadView = () => {
72810
72870
  "button",
72811
72871
  {
72812
72872
  ref: filterButtonRef,
72813
- onClick: () => setIsFilterOpen(!isFilterOpen),
72873
+ onClick: handleFilterToggle,
72814
72874
  className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || trendMode !== "all" ? "border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-500" : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50"}`,
72815
72875
  "aria-label": "Open filters",
72816
72876
  children: [
@@ -72843,7 +72903,7 @@ var PlantHeadView = () => {
72843
72903
  "select",
72844
72904
  {
72845
72905
  value: trendMode,
72846
- onChange: (e) => setTrendMode(e.target.value),
72906
+ onChange: handleTrendModeChange,
72847
72907
  className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
72848
72908
  style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.75rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
72849
72909
  children: [
@@ -72905,7 +72965,7 @@ var PlantHeadView = () => {
72905
72965
  "button",
72906
72966
  {
72907
72967
  type: "button",
72908
- onClick: () => setPoorestLineMode("output"),
72968
+ onClick: () => handlePoorestLineModeChange("output"),
72909
72969
  className: `px-3 py-1 text-[11px] font-bold rounded-md ${poorestLineMode === "output" ? "bg-white text-slate-800 shadow-sm" : "text-slate-500 hover:text-slate-800"}`,
72910
72970
  children: "Output"
72911
72971
  }
@@ -72914,7 +72974,7 @@ var PlantHeadView = () => {
72914
72974
  "button",
72915
72975
  {
72916
72976
  type: "button",
72917
- onClick: () => setPoorestLineMode("uptime"),
72977
+ onClick: () => handlePoorestLineModeChange("uptime"),
72918
72978
  className: `px-3 py-1 text-[11px] font-bold rounded-md ${poorestLineMode === "uptime" ? "bg-white text-slate-800 shadow-sm" : "text-slate-500 hover:text-slate-800"}`,
72919
72979
  children: "Uptime"
72920
72980
  }
@@ -72925,7 +72985,10 @@ var PlantHeadView = () => {
72925
72985
  "button",
72926
72986
  {
72927
72987
  type: "button",
72928
- onClick: () => navigate("/kpis"),
72988
+ onClick: () => {
72989
+ trackCoreEvent("Operations Overview View All Clicked", { section: "poorest_performers" });
72990
+ navigate("/kpis?tab=leaderboard");
72991
+ },
72929
72992
  className: "text-[11px] font-bold text-slate-500 hover:text-slate-800 transition-colors",
72930
72993
  children: "View All"
72931
72994
  }
@@ -72941,7 +73004,10 @@ var PlantHeadView = () => {
72941
73004
  return /* @__PURE__ */ jsxRuntime.jsx(
72942
73005
  "div",
72943
73006
  {
72944
- onClick: () => navigate(`/kpis/${line.id}`),
73007
+ onClick: () => {
73008
+ trackCoreEvent("Operations Overview Line Clicked", { line_id: line.id, line_name: line.name });
73009
+ navigate(`/kpis/${line.id}`);
73010
+ },
72945
73011
  className: "block py-3 hover:bg-slate-50/50 transition-colors cursor-pointer group relative",
72946
73012
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4", children: [
72947
73013
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
@@ -72981,7 +73047,10 @@ var PlantHeadView = () => {
72981
73047
  className: "bg-white rounded-xl shadow-sm border border-slate-100 flex flex-col overflow-hidden text-left",
72982
73048
  children: [
72983
73049
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4 flex-none flex justify-between items-center border-b border-slate-50/50 relative", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Idle Time Breakdown" }) }),
72984
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 p-4 pt-2 relative", children: isIdleReasonBreakdownLoading ? /* @__PURE__ */ jsxRuntime.jsx(OverviewIdleBreakdownSkeleton, {}) : /* @__PURE__ */ jsxRuntime.jsx(
73050
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0 p-4 pt-2 relative", children: isIdleReasonBreakdownLoading ? /* @__PURE__ */ jsxRuntime.jsx(OverviewIdleBreakdownSkeleton, {}) : showIdleModuleNotEnabledState ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full flex items-center justify-center rounded-xl border border-dashed border-slate-200 bg-slate-50/80 px-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
73051
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-slate-700", children: "Module not enabled" }),
73052
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-slate-500", children: "Enable idle-time classification on at least one line to view this breakdown." })
73053
+ ] }) }) : /* @__PURE__ */ jsxRuntime.jsx(
72985
73054
  IdleTimeReasonChart,
72986
73055
  {
72987
73056
  data: idleBreakdown,
@@ -73010,7 +73079,8 @@ var PlantHeadView = () => {
73010
73079
  yAxisDomain: [0, 100],
73011
73080
  showLegend: false,
73012
73081
  showGrid: true,
73013
- fillContainer: true
73082
+ fillContainer: true,
73083
+ tooltipLabelFormatter: trendTooltipLabelFormatter
73014
73084
  },
73015
73085
  trendPlayKey
73016
73086
  ) }) })
@@ -73024,7 +73094,10 @@ var PlantHeadView = () => {
73024
73094
  "button",
73025
73095
  {
73026
73096
  type: "button",
73027
- onClick: () => navigate("/improvement-center"),
73097
+ onClick: () => {
73098
+ trackCoreEvent("Operations Overview View All Clicked", { section: "improvements" });
73099
+ navigate("/improvement-center");
73100
+ },
73028
73101
  className: "text-[11px] font-bold text-slate-500 hover:text-slate-800 transition-colors",
73029
73102
  children: "View All"
73030
73103
  }
@@ -73033,7 +73106,10 @@ var PlantHeadView = () => {
73033
73106
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 p-0 flex flex-col px-5 py-2 justify-start overflow-auto", children: isImprovementsLoading ? /* @__PURE__ */ jsxRuntime.jsx(OverviewImprovementsSkeleton, {}) : improvements.length > 0 ? improvements.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
73034
73107
  "div",
73035
73108
  {
73036
- onClick: () => navigate("/improvement-center"),
73109
+ onClick: () => {
73110
+ trackCoreEvent("Operations Overview Improvement Clicked", { issue_id: item.id, title: item.title });
73111
+ navigate("/improvement-center");
73112
+ },
73037
73113
  className: "flex items-center justify-between py-3 border-b border-slate-50 last:border-0 group cursor-pointer",
73038
73114
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 flex-1 min-w-0 pr-4", children: [
73039
73115
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-500 transition-colors" }) }),
package/dist/index.mjs CHANGED
@@ -29768,6 +29768,7 @@ var LineChartComponent = ({
29768
29768
  yAxisUnit,
29769
29769
  yAxisDomain,
29770
29770
  tooltipFormatter,
29771
+ tooltipLabelFormatter,
29771
29772
  legendPayload,
29772
29773
  className,
29773
29774
  showGrid = true,
@@ -29847,7 +29848,15 @@ var LineChartComponent = ({
29847
29848
  stroke: axisStrokeColor
29848
29849
  }
29849
29850
  ),
29850
- showTooltip && /* @__PURE__ */ jsx(Tooltip, { formatter: tooltipFormatter || defaultTooltipFormatter, itemStyle: { color: "#111827" }, cursor: { strokeDasharray: "3 3" } }),
29851
+ showTooltip && /* @__PURE__ */ jsx(
29852
+ Tooltip,
29853
+ {
29854
+ formatter: tooltipFormatter || defaultTooltipFormatter,
29855
+ labelFormatter: tooltipLabelFormatter,
29856
+ itemStyle: { color: "#111827" },
29857
+ cursor: { strokeDasharray: "3 3" }
29858
+ }
29859
+ ),
29851
29860
  showLegend && /* @__PURE__ */ jsx(Legend, { payload: legendPayload }),
29852
29861
  lines.map((lineConfig, index) => {
29853
29862
  const lineProps = {
@@ -29903,7 +29912,7 @@ var LineChart = React141__default.memo(LineChartComponent, (prevProps, nextProps
29903
29912
  })) {
29904
29913
  return false;
29905
29914
  }
29906
- if (prevProps.tooltipFormatter !== nextProps.tooltipFormatter || prevProps.xAxisTickFormatter !== nextProps.xAxisTickFormatter || JSON.stringify(prevProps.legendPayload) !== JSON.stringify(nextProps.legendPayload)) {
29915
+ if (prevProps.tooltipFormatter !== nextProps.tooltipFormatter || prevProps.xAxisTickFormatter !== nextProps.xAxisTickFormatter || prevProps.tooltipLabelFormatter !== nextProps.tooltipLabelFormatter || JSON.stringify(prevProps.legendPayload) !== JSON.stringify(nextProps.legendPayload)) {
29907
29916
  return false;
29908
29917
  }
29909
29918
  return true;
@@ -42618,22 +42627,16 @@ var formatDuration = (seconds) => {
42618
42627
  const hours = Math.floor(wholeSeconds % 86400 / 3600);
42619
42628
  const minutes = Math.floor(wholeSeconds % 3600 / 60);
42620
42629
  const remainingSeconds = wholeSeconds % 60;
42621
- if (days > 0) {
42622
- return `${days}d ${hours}h ${minutes}m ${remainingSeconds}s`;
42623
- }
42624
- if (hours > 0) {
42625
- return `${hours}h ${minutes}m ${remainingSeconds}s`;
42626
- }
42627
- if (minutes > 0) {
42628
- return `${minutes}m ${remainingSeconds}s`;
42629
- }
42630
- return `${remainingSeconds}s`;
42631
- };
42632
- var formatPercentage = (value) => {
42633
- if (value === null || value === void 0 || !Number.isFinite(value)) {
42634
- return "--";
42630
+ const parts = [];
42631
+ if (days > 0) parts.push(`${days}d`);
42632
+ if (hours > 0) parts.push(`${hours}h`);
42633
+ if (minutes > 0) parts.push(`${minutes}m`);
42634
+ if (days === 0 && hours === 0) {
42635
+ if (remainingSeconds > 0 || parts.length === 0) {
42636
+ parts.push(`${remainingSeconds}s`);
42637
+ }
42635
42638
  }
42636
- return `${value.toFixed(1)}%`;
42639
+ return parts.join(" ");
42637
42640
  };
42638
42641
  var getColorForEntry = (name, index) => {
42639
42642
  const normalized = name.trim().toLowerCase();
@@ -42653,30 +42656,35 @@ var CustomTooltip = ({ active, payload, hideTotalDuration }) => {
42653
42656
  if (active && payload && payload.length) {
42654
42657
  const datum = payload[0]?.payload;
42655
42658
  const contributors = datum?.contributors || [];
42656
- const fill = payload[0]?.payload?.fill || payload[0]?.fill || getColorForEntry(payload[0].name, 0);
42657
- return /* @__PURE__ */ jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 border border-gray-200 shadow-xl rounded-lg z-50 min-w-[200px]", children: [
42658
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mb-2 pb-1 border-b border-gray-100", children: [
42659
- /* @__PURE__ */ jsx("div", { className: "w-2.5 h-2.5 rounded-full flex-shrink-0", style: { backgroundColor: fill } }),
42660
- /* @__PURE__ */ jsx("span", { className: "font-semibold text-slate-800 text-sm", children: payload[0].name.replace(/_/g, " ") })
42659
+ let effContributionStr = "--";
42660
+ if (datum?.efficiencyLossPercentage !== null && datum?.efficiencyLossPercentage !== void 0 && Number.isFinite(datum?.efficiencyLossPercentage)) {
42661
+ const val = datum.efficiencyLossPercentage;
42662
+ effContributionStr = val === 0 ? "0.0%" : `${Math.abs(val).toFixed(1)}%`;
42663
+ }
42664
+ const hasContributors = contributors.length > 0;
42665
+ return /* @__PURE__ */ jsxs("div", { className: "bg-white p-3 border border-gray-100 shadow-lg rounded-lg text-xs z-50 min-w-[280px]", children: [
42666
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 pb-2 mb-2 border-b border-slate-100", children: [
42667
+ /* @__PURE__ */ jsx("div", { className: "w-2.5 h-2.5 rounded-full flex-shrink-0", style: { backgroundColor: payload[0].payload.fill || getColorForEntry(payload[0].name, 0) } }),
42668
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-slate-800 text-[13px]", children: payload[0].name.replace(/_/g, " ") })
42661
42669
  ] }),
42662
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 text-xs", children: [
42663
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center gap-3", children: [
42670
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 pb-1", children: [
42671
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center gap-4", children: [
42664
42672
  /* @__PURE__ */ jsx("span", { className: "text-slate-500", children: "Share of idle time" }),
42665
- /* @__PURE__ */ jsx("span", { className: "font-medium text-slate-800", children: `${payload[0].value.toFixed(1)}%` })
42673
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-slate-800", children: `${payload[0].value.toFixed(1)}%` })
42666
42674
  ] }),
42667
- !hideTotalDuration && datum?.totalDurationSeconds !== void 0 ? /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center gap-3", children: [
42675
+ !hideTotalDuration && !hasContributors && datum?.totalDurationSeconds !== void 0 ? /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center gap-4", children: [
42668
42676
  /* @__PURE__ */ jsx("span", { className: "text-slate-500", children: "Total Duration" }),
42669
- /* @__PURE__ */ jsx("span", { className: "font-medium text-slate-800", children: formatDuration(datum.totalDurationSeconds) })
42677
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-slate-800", children: formatDuration(datum.totalDurationSeconds) })
42670
42678
  ] }) : null,
42671
- /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center gap-3", children: [
42679
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center gap-4", children: [
42672
42680
  /* @__PURE__ */ jsx("span", { className: "text-slate-500", children: "Efficiency Loss" }),
42673
- /* @__PURE__ */ jsx("span", { className: "font-medium text-slate-800", children: formatPercentage(datum?.efficiencyLossPercentage) })
42681
+ /* @__PURE__ */ jsx("span", { className: "font-semibold text-slate-800", children: effContributionStr })
42674
42682
  ] })
42675
42683
  ] }),
42676
- contributors.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-2 pt-2 border-t border-gray-100", children: [
42677
- /* @__PURE__ */ jsx("p", { className: "text-[9px] font-semibold uppercase tracking-wider text-slate-400 mb-1.5", children: "Top Workstations" }),
42678
- /* @__PURE__ */ jsx("div", { className: "space-y-1", children: contributors.slice(0, 3).map((contributor) => /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 text-[10px]", children: [
42679
- /* @__PURE__ */ jsx("span", { className: "text-slate-700 truncate max-w-[120px]", children: contributor.workspaceName }),
42684
+ hasContributors ? /* @__PURE__ */ jsxs("div", { className: "mt-2 pt-2 border-t border-slate-100", children: [
42685
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-2", children: "TOP CONTRIBUTORS" }),
42686
+ /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: contributors.slice(0, 3).map((contributor) => /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
42687
+ /* @__PURE__ */ jsx("span", { className: "text-slate-600 truncate max-w-[170px]", title: contributor.workspaceName, children: contributor.workspaceName }),
42680
42688
  /* @__PURE__ */ jsx("span", { className: "text-slate-500 text-right whitespace-nowrap", children: `${formatDuration(contributor.totalDurationSeconds)} \u2022 ${Number(contributor.percentageWithinReason || 0).toFixed(1)}%` })
42681
42689
  ] }, `${payload[0].name}-${contributor.workspaceId}`)) })
42682
42690
  ] }) : null
@@ -49357,10 +49365,11 @@ var AlertsPopup = ({
49357
49365
  const updatePosition = useCallback(() => {
49358
49366
  const viewportHeight = window.innerHeight;
49359
49367
  const viewportWidth = window.innerWidth;
49368
+ const currentPopupHeight = popupRef.current?.offsetHeight || 280;
49360
49369
  if (viewportWidth < 768) {
49361
- const mobileWidth = Math.max(240, Math.min(360, viewportWidth - 32));
49370
+ const mobileWidth = Math.max(240, Math.min(380, viewportWidth - 32));
49362
49371
  setPosition({
49363
- top: Math.max(16, (viewportHeight - 280) / 2),
49372
+ top: Math.max(16, (viewportHeight - currentPopupHeight) / 2),
49364
49373
  left: Math.max(16, (viewportWidth - mobileWidth) / 2),
49365
49374
  width: mobileWidth
49366
49375
  });
@@ -49368,22 +49377,25 @@ var AlertsPopup = ({
49368
49377
  }
49369
49378
  if (!triggerRef.current) return;
49370
49379
  const rect = triggerRef.current.getBoundingClientRect();
49371
- const popupHeight = 280;
49372
- let top = rect.bottom - popupHeight + 40;
49373
- if (top < 16) top = 16;
49374
- if (top + popupHeight > viewportHeight - 16) {
49375
- top = viewportHeight - popupHeight - 16;
49376
- }
49380
+ const bottomOffset = viewportHeight - rect.bottom + 12;
49377
49381
  setPosition({
49378
- top,
49382
+ bottom: bottomOffset,
49379
49383
  left: 88,
49380
49384
  // Sidebar width (80px) + gap (8px)
49381
- width: 360
49385
+ width: 400
49386
+ // Make it a bit wider for enterprise/cleaner look
49382
49387
  });
49383
49388
  }, [triggerRef]);
49384
49389
  useEffect(() => {
49385
49390
  if (isOpen) {
49386
- updatePosition();
49391
+ const timer = setTimeout(() => {
49392
+ updatePosition();
49393
+ }, 0);
49394
+ return () => clearTimeout(timer);
49395
+ }
49396
+ }, [isOpen, alerts, isLoading, error, updatePosition]);
49397
+ useEffect(() => {
49398
+ if (isOpen) {
49387
49399
  window.addEventListener("resize", updatePosition);
49388
49400
  window.addEventListener("scroll", updatePosition);
49389
49401
  }
@@ -49429,19 +49441,21 @@ var AlertsPopup = ({
49429
49441
  },
49430
49442
  style: {
49431
49443
  position: "fixed",
49432
- top: position.top,
49444
+ ...position.top !== void 0 ? { top: position.top } : {},
49445
+ ...position.bottom !== void 0 ? { bottom: position.bottom } : {},
49433
49446
  left: position.left,
49434
49447
  width: position.width,
49448
+ maxHeight: "calc(100vh - 32px)",
49435
49449
  zIndex: 2147483647
49436
49450
  },
49437
- className: "bg-white rounded-[16px] shadow-xl border border-gray-100 max-w-[calc(100vw-32px)] overflow-hidden flex flex-col",
49451
+ className: "bg-white rounded-[16px] shadow-2xl border border-gray-100 max-w-[calc(100vw-32px)] max-h-[calc(100vh-80px)] overflow-hidden flex flex-col",
49438
49452
  children: [
49439
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-gray-50 bg-[#fafafa]", children: [
49453
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-gray-50 bg-[#fafafa] shrink-0", children: [
49440
49454
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
49441
- /* @__PURE__ */ jsx("h3", { className: "text-[14px] font-bold text-[#111827] uppercase tracking-wide", children: "Recent Alerts" }),
49455
+ /* @__PURE__ */ jsx("h3", { className: "text-[14px] font-bold text-[#111827] uppercase tracking-wide", children: "Current Alerts" }),
49442
49456
  alertsCount > 0 && /* @__PURE__ */ jsxs("span", { className: "bg-[#eff6ff] text-[#2563eb] text-[11px] font-bold px-2 py-0.5 rounded-full", children: [
49443
49457
  alertsCount,
49444
- " Active"
49458
+ " alerts"
49445
49459
  ] })
49446
49460
  ] }),
49447
49461
  /* @__PURE__ */ jsx(
@@ -49453,13 +49467,13 @@ var AlertsPopup = ({
49453
49467
  }
49454
49468
  )
49455
49469
  ] }),
49456
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col max-h-[380px] overflow-y-auto", children: [
49457
- isLoading && /* @__PURE__ */ jsx("div", { className: "px-5 py-10 text-sm text-gray-500", children: "Loading current shift alerts..." }),
49470
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-h-0 overflow-y-auto max-h-[500px]", children: [
49471
+ isLoading && /* @__PURE__ */ jsx("div", { className: "px-5 py-10 text-sm text-gray-500", children: "Loading alerts..." }),
49458
49472
  !isLoading && error && /* @__PURE__ */ jsx("div", { className: "px-5 py-10 text-sm text-red-600", children: error }),
49459
- !isLoading && !error && alerts.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-5 py-10 text-sm text-gray-500", children: "No active alerts for the current shift." }),
49473
+ !isLoading && !error && alerts.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-5 py-10 text-sm text-gray-500", children: "No alerts right now." }),
49460
49474
  !isLoading && !error && alerts.map((alert2, index) => {
49461
49475
  const isCritical = typeof alert2.delta_efficiency === "number" && alert2.delta_efficiency <= -10;
49462
- const description = `${alert2.monitoring_mode === "uptime" ? "Uptime" : "Output"} efficiency is ${formatPercent(alert2.current_efficiency)} vs last week's ${formatPercent(alert2.baseline_efficiency)} average.`;
49476
+ const description = `Now: ${formatPercent(alert2.current_efficiency)}. Usual for this shift: ${formatPercent(alert2.baseline_efficiency)}.`;
49463
49477
  return /* @__PURE__ */ jsx(
49464
49478
  motion.div,
49465
49479
  {
@@ -49476,20 +49490,13 @@ var AlertsPopup = ({
49476
49490
  /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
49477
49491
  /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-start mb-1.5", children: [
49478
49492
  /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold text-[#111827]", children: alert2.line_name }),
49479
- /* @__PURE__ */ jsx("span", { className: "text-[12px] text-[#94a3b8] font-medium whitespace-nowrap ml-3", children: formatRelativeTime2(alert2.evaluated_at) })
49480
- ] }),
49481
- /* @__PURE__ */ jsxs("p", { className: "text-[12px] font-medium text-[#475569] uppercase tracking-wide", children: [
49482
- alert2.monitoring_mode === "uptime" ? "Uptime" : "Output",
49483
- " regression"
49493
+ /* @__PURE__ */ jsxs("span", { className: "text-[12px] text-[#94a3b8] font-medium whitespace-nowrap ml-3", children: [
49494
+ "Checked ",
49495
+ formatRelativeTime2(alert2.evaluated_at)
49496
+ ] })
49484
49497
  ] }),
49485
- /* @__PURE__ */ jsx("p", { className: "text-[13.5px] text-[#64748b] leading-[1.45] mt-1", children: description }),
49486
- /* @__PURE__ */ jsxs("p", { className: "text-[12px] text-[#94a3b8] mt-2", children: [
49487
- "Delta ",
49488
- formatPercent(alert2.delta_efficiency),
49489
- " across ",
49490
- alert2.baseline_sample_size,
49491
- " historical shifts"
49492
- ] })
49498
+ /* @__PURE__ */ jsx("p", { className: "text-[12px] font-medium text-[#475569] uppercase tracking-wide", children: alert2.monitoring_mode === "uptime" ? "Machine Utilization is low" : "Output is low" }),
49499
+ /* @__PURE__ */ jsx("p", { className: "text-[13.5px] text-[#64748b] leading-[1.45] mt-1", children: description })
49493
49500
  ] })
49494
49501
  ] })
49495
49502
  },
@@ -49552,7 +49559,7 @@ var SideNavBar = memo$1(({
49552
49559
  const handleHomeClick = useCallback(() => {
49553
49560
  navigate("/", {
49554
49561
  trackingEvent: {
49555
- name: "Line Overview Page Clicked",
49562
+ name: "Operations Overview Page Clicked",
49556
49563
  properties: {
49557
49564
  source: "side_nav",
49558
49565
  line_id: lineId,
@@ -49840,6 +49847,7 @@ var SideNavBar = memo$1(({
49840
49847
  }
49841
49848
  }, [signOut]);
49842
49849
  const handleLogoClick = useCallback(() => {
49850
+ trackCoreEvent("Operations Overview Page Clicked", { source: "logo" });
49843
49851
  navigate("/");
49844
49852
  onMobileMenuClose?.();
49845
49853
  }, [navigate, onMobileMenuClose]);
@@ -49992,9 +50000,11 @@ var SideNavBar = memo$1(({
49992
50000
  {
49993
50001
  ref: alertsTriggerRef,
49994
50002
  onClick: () => {
49995
- setIsAlertsOpen(!isAlertsOpen);
50003
+ const willOpen = !isAlertsOpen;
50004
+ setIsAlertsOpen(willOpen);
49996
50005
  setIsSettingsOpen(false);
49997
- if (!isAlertsOpen) {
50006
+ if (willOpen) {
50007
+ trackCoreEvent("Alerts Page Clicked", { source: "side_nav" });
49998
50008
  void refreshAlertsSummary();
49999
50009
  }
50000
50010
  },
@@ -50152,6 +50162,7 @@ var SideNavBar = memo$1(({
50152
50162
  {
50153
50163
  onClick: () => {
50154
50164
  setIsAlertsOpen(true);
50165
+ trackCoreEvent("Alerts Page Clicked", { source: "side_nav_mobile" });
50155
50166
  void refreshAlertsSummary();
50156
50167
  onMobileMenuClose?.();
50157
50168
  },
@@ -59961,6 +59972,7 @@ var KPIsOverviewView = ({
59961
59972
  backLinkUrl,
59962
59973
  lineIds
59963
59974
  }) => {
59975
+ const router = useRouter();
59964
59976
  const [lines, setLines] = useState([]);
59965
59977
  const [leaderboardLines, setLeaderboardLines] = useState([]);
59966
59978
  const [leaderboardLinesLoading, setLeaderboardLinesLoading] = useState(false);
@@ -59988,6 +60000,13 @@ var KPIsOverviewView = ({
59988
60000
  const [monthlyError, setMonthlyError] = useState(null);
59989
60001
  const dailyRequestKeyRef = useRef(null);
59990
60002
  const monthlyRequestKeyRef = useRef(null);
60003
+ useEffect(() => {
60004
+ if (!router.isReady) return;
60005
+ const tab = router.query.tab;
60006
+ if (tab === "leaderboard") {
60007
+ setActiveTab("leaderboard");
60008
+ }
60009
+ }, [router.isReady, router.query.tab]);
59991
60010
  const supabase = useSupabase();
59992
60011
  const { user } = useAuth();
59993
60012
  const dashboardConfig = useDashboardConfig();
@@ -72256,13 +72275,7 @@ var formatIdleDuration = (seconds) => {
72256
72275
  if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
72257
72276
  return "--";
72258
72277
  }
72259
- const wholeSeconds = Math.max(0, Math.round(seconds));
72260
- const minutes = Math.floor(wholeSeconds / 60);
72261
- const remainingSeconds = wholeSeconds % 60;
72262
- if (minutes > 0) {
72263
- return `${minutes}m ${remainingSeconds}s`;
72264
- }
72265
- return `${remainingSeconds}s`;
72278
+ return formatIdleTime(Math.max(0, Math.round(seconds)));
72266
72279
  };
72267
72280
  var formatSignedIdleDuration = (seconds) => {
72268
72281
  const sign = seconds > 0 ? "+" : "-";
@@ -72389,6 +72402,7 @@ var PlantHeadView = () => {
72389
72402
  const [trendFetchNonce, setTrendFetchNonce] = React141__default.useState(0);
72390
72403
  const [trendViewOpenNonce] = React141__default.useState(() => Date.now());
72391
72404
  const [idleReasonBreakdown, setIdleReasonBreakdown] = React141__default.useState([]);
72405
+ const [idleReasonScope, setIdleReasonScope] = React141__default.useState(null);
72392
72406
  const [isIdleReasonBreakdownLoading, setIsIdleReasonBreakdownLoading] = React141__default.useState(false);
72393
72407
  const [improvements, setImprovements] = React141__default.useState([]);
72394
72408
  const [isImprovementsLoading, setIsImprovementsLoading] = React141__default.useState(false);
@@ -72396,6 +72410,35 @@ var PlantHeadView = () => {
72396
72410
  const filterRef = React141__default.useRef(null);
72397
72411
  const filterButtonRef = React141__default.useRef(null);
72398
72412
  const mobileFilterButtonRef = React141__default.useRef(null);
72413
+ React141__default.useEffect(() => {
72414
+ trackCorePageView("Operations Overview");
72415
+ }, []);
72416
+ const handleDateRangeChange = React141__default.useCallback((range) => {
72417
+ trackCoreEvent("Operations Overview Date Range Changed", {
72418
+ start_date: range.startKey,
72419
+ end_date: range.endKey
72420
+ });
72421
+ setDateRange(range);
72422
+ }, []);
72423
+ const handleFilterToggle = React141__default.useCallback(() => {
72424
+ trackCoreEvent("Operations Overview Filter Toggled", {
72425
+ action: !isFilterOpen ? "open" : "close"
72426
+ });
72427
+ setIsFilterOpen(!isFilterOpen);
72428
+ }, [isFilterOpen]);
72429
+ const handleTrendModeChange = React141__default.useCallback((e) => {
72430
+ const newMode = e.target.value;
72431
+ trackCoreEvent("Operations Overview Shift Filter Changed", {
72432
+ shift_mode: newMode
72433
+ });
72434
+ setTrendMode(newMode);
72435
+ }, []);
72436
+ const handlePoorestLineModeChange = React141__default.useCallback((mode) => {
72437
+ trackCoreEvent("Operations Overview Poorest Line Mode Changed", {
72438
+ mode
72439
+ });
72440
+ setPoorestLineMode(mode);
72441
+ }, []);
72399
72442
  React141__default.useEffect(() => {
72400
72443
  function handleClickOutside(event) {
72401
72444
  if (filterRef.current && !filterRef.current.contains(event.target) && filterButtonRef.current && !filterButtonRef.current.contains(event.target) && mobileFilterButtonRef.current && !mobileFilterButtonRef.current.contains(event.target)) {
@@ -72545,11 +72588,13 @@ var PlantHeadView = () => {
72545
72588
  React141__default.useEffect(() => {
72546
72589
  if (!supabase || !companyId) return void 0;
72547
72590
  if (scopedLineIds.length === 0) {
72591
+ setIdleReasonScope(null);
72548
72592
  setIdleReasonBreakdown([]);
72549
72593
  setIsIdleReasonBreakdownLoading(false);
72550
72594
  return void 0;
72551
72595
  }
72552
72596
  let cancelled = false;
72597
+ setIdleReasonScope(null);
72553
72598
  setIdleReasonBreakdown([]);
72554
72599
  setIsIdleReasonBreakdownLoading(true);
72555
72600
  const fetchIdleReasonBreakdown = async () => {
@@ -72565,12 +72610,14 @@ var PlantHeadView = () => {
72565
72610
  `/api/dashboard/operations-overview/idle-reason-breakdown?${params.toString()}`
72566
72611
  );
72567
72612
  if (!cancelled) {
72613
+ setIdleReasonScope(response.scope || null);
72568
72614
  setIdleReasonBreakdown(response.idle_reason_breakdown || []);
72569
72615
  }
72570
72616
  };
72571
72617
  fetchIdleReasonBreakdown().catch((error) => {
72572
72618
  console.error("[PlantHeadView] Failed to fetch idle reason breakdown", error);
72573
72619
  if (!cancelled) {
72620
+ setIdleReasonScope(null);
72574
72621
  setIdleReasonBreakdown([]);
72575
72622
  }
72576
72623
  }).finally(() => {
@@ -72666,13 +72713,17 @@ var PlantHeadView = () => {
72666
72713
  });
72667
72714
  }, [comparisonLabel, overview.summary?.avg_idle_per_workstation?.delta_seconds]);
72668
72715
  const trendData = React141__default.useMemo(() => {
72669
- return (overview.trend?.points || []).map((point) => ({
72670
- name: point.date ? format(parseDateKeyToDate(point.date), "MMM d") : "",
72671
- efficiency: (() => {
72672
- const value = toNumber3(point.avg_efficiency);
72673
- return value === null ? void 0 : value;
72674
- })()
72675
- }));
72716
+ return (overview.trend?.points || []).map((point) => {
72717
+ const pointDate = point.date ? parseDateKeyToDate(point.date) : null;
72718
+ return {
72719
+ name: pointDate ? format(pointDate, "MMM d") : "",
72720
+ dayOfWeek: pointDate ? format(pointDate, "EEEE") : "",
72721
+ efficiency: (() => {
72722
+ const value = toNumber3(point.avg_efficiency);
72723
+ return value === null ? void 0 : value;
72724
+ })()
72725
+ };
72726
+ });
72676
72727
  }, [overview.trend?.points]);
72677
72728
  const trendPlayKey = React141__default.useMemo(() => {
72678
72729
  return `${trendViewOpenNonce}:${trendFetchNonce}`;
@@ -72691,6 +72742,10 @@ var PlantHeadView = () => {
72691
72742
  }))
72692
72743
  })).filter((item) => item.value > 0);
72693
72744
  }, [idleReasonBreakdown]);
72745
+ const showIdleModuleNotEnabledState = React141__default.useMemo(() => {
72746
+ const enabledLineCount = idleReasonScope?.idle_time_vlm_enabled_line_count;
72747
+ return !isIdleReasonBreakdownLoading && scopedLineIds.length > 0 && typeof enabledLineCount === "number" && enabledLineCount === 0;
72748
+ }, [idleReasonScope?.idle_time_vlm_enabled_line_count, isIdleReasonBreakdownLoading, scopedLineIds.length]);
72694
72749
  const mergedPoorestLines = React141__default.useMemo(() => {
72695
72750
  const rows = overview.poorest_lines?.[poorestLineMode] || [];
72696
72751
  return rows.slice(0, 3).map((line) => {
@@ -72711,6 +72766,11 @@ var PlantHeadView = () => {
72711
72766
  const showPoorestModeToggle = !!availableLineModes?.has_output && !!availableLineModes?.has_uptime;
72712
72767
  availableLineModes?.has_uptime && !availableLineModes?.has_output ? "Uptime" : "Output";
72713
72768
  const poorestMetricLabel = poorestLineMode === "uptime" ? "Uptime" : "Efficiency";
72769
+ const trendTooltipLabelFormatter = React141__default.useCallback((label, payload) => {
72770
+ const dayOfWeek = payload?.[0]?.payload?.dayOfWeek;
72771
+ if (!dayOfWeek || typeof label !== "string") return label;
72772
+ return `${label} (${dayOfWeek})`;
72773
+ }, []);
72714
72774
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-h-screen bg-slate-50 w-full font-sans", children: [
72715
72775
  /* @__PURE__ */ jsx("header", { className: "sticky top-0 z-10 bg-white border-b flex-shrink-0 shadow-sm", children: /* @__PURE__ */ jsxs("div", { className: "px-3 sm:px-4 md:px-6 py-2 sm:py-3 relative", children: [
72716
72776
  /* @__PURE__ */ jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
@@ -72737,7 +72797,7 @@ var PlantHeadView = () => {
72737
72797
  year: parseDateKeyToDate(dateRange.startKey).getFullYear(),
72738
72798
  timezone: appTimezone,
72739
72799
  value: dateRange,
72740
- onChange: setDateRange,
72800
+ onChange: handleDateRangeChange,
72741
72801
  showLabel: false
72742
72802
  }
72743
72803
  ),
@@ -72745,7 +72805,7 @@ var PlantHeadView = () => {
72745
72805
  "button",
72746
72806
  {
72747
72807
  ref: mobileFilterButtonRef,
72748
- onClick: () => setIsFilterOpen(!isFilterOpen),
72808
+ onClick: handleFilterToggle,
72749
72809
  className: `p-2 rounded-full transition-colors relative ${isFilterOpen || trendMode !== "all" ? "bg-blue-50" : "active:bg-gray-100"}`,
72750
72810
  "aria-label": "Open filters",
72751
72811
  children: [
@@ -72773,7 +72833,7 @@ var PlantHeadView = () => {
72773
72833
  year: parseDateKeyToDate(dateRange.startKey).getFullYear(),
72774
72834
  timezone: appTimezone,
72775
72835
  value: dateRange,
72776
- onChange: setDateRange,
72836
+ onChange: handleDateRangeChange,
72777
72837
  showLabel: false
72778
72838
  }
72779
72839
  ) }),
@@ -72781,7 +72841,7 @@ var PlantHeadView = () => {
72781
72841
  "button",
72782
72842
  {
72783
72843
  ref: filterButtonRef,
72784
- onClick: () => setIsFilterOpen(!isFilterOpen),
72844
+ onClick: handleFilterToggle,
72785
72845
  className: `flex items-center gap-2 px-3 py-1.5 rounded-lg border text-sm font-medium transition-all shadow-sm ${isFilterOpen || trendMode !== "all" ? "border-blue-500 bg-blue-50 text-blue-700 ring-1 ring-blue-500" : "border-slate-200 bg-white text-slate-700 hover:bg-slate-50"}`,
72786
72846
  "aria-label": "Open filters",
72787
72847
  children: [
@@ -72814,7 +72874,7 @@ var PlantHeadView = () => {
72814
72874
  "select",
72815
72875
  {
72816
72876
  value: trendMode,
72817
- onChange: (e) => setTrendMode(e.target.value),
72877
+ onChange: handleTrendModeChange,
72818
72878
  className: "w-full appearance-none pl-3 pr-8 py-2 text-sm bg-gray-50 border border-gray-200 hover:border-gray-300 rounded-lg text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:bg-white transition-all cursor-pointer",
72819
72879
  style: { backgroundImage: `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`, backgroundPosition: `right 0.75rem center`, backgroundRepeat: `no-repeat`, backgroundSize: `1.2em 1.2em` },
72820
72880
  children: [
@@ -72876,7 +72936,7 @@ var PlantHeadView = () => {
72876
72936
  "button",
72877
72937
  {
72878
72938
  type: "button",
72879
- onClick: () => setPoorestLineMode("output"),
72939
+ onClick: () => handlePoorestLineModeChange("output"),
72880
72940
  className: `px-3 py-1 text-[11px] font-bold rounded-md ${poorestLineMode === "output" ? "bg-white text-slate-800 shadow-sm" : "text-slate-500 hover:text-slate-800"}`,
72881
72941
  children: "Output"
72882
72942
  }
@@ -72885,7 +72945,7 @@ var PlantHeadView = () => {
72885
72945
  "button",
72886
72946
  {
72887
72947
  type: "button",
72888
- onClick: () => setPoorestLineMode("uptime"),
72948
+ onClick: () => handlePoorestLineModeChange("uptime"),
72889
72949
  className: `px-3 py-1 text-[11px] font-bold rounded-md ${poorestLineMode === "uptime" ? "bg-white text-slate-800 shadow-sm" : "text-slate-500 hover:text-slate-800"}`,
72890
72950
  children: "Uptime"
72891
72951
  }
@@ -72896,7 +72956,10 @@ var PlantHeadView = () => {
72896
72956
  "button",
72897
72957
  {
72898
72958
  type: "button",
72899
- onClick: () => navigate("/kpis"),
72959
+ onClick: () => {
72960
+ trackCoreEvent("Operations Overview View All Clicked", { section: "poorest_performers" });
72961
+ navigate("/kpis?tab=leaderboard");
72962
+ },
72900
72963
  className: "text-[11px] font-bold text-slate-500 hover:text-slate-800 transition-colors",
72901
72964
  children: "View All"
72902
72965
  }
@@ -72912,7 +72975,10 @@ var PlantHeadView = () => {
72912
72975
  return /* @__PURE__ */ jsx(
72913
72976
  "div",
72914
72977
  {
72915
- onClick: () => navigate(`/kpis/${line.id}`),
72978
+ onClick: () => {
72979
+ trackCoreEvent("Operations Overview Line Clicked", { line_id: line.id, line_name: line.name });
72980
+ navigate(`/kpis/${line.id}`);
72981
+ },
72916
72982
  className: "block py-3 hover:bg-slate-50/50 transition-colors cursor-pointer group relative",
72917
72983
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
72918
72984
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
@@ -72952,7 +73018,10 @@ var PlantHeadView = () => {
72952
73018
  className: "bg-white rounded-xl shadow-sm border border-slate-100 flex flex-col overflow-hidden text-left",
72953
73019
  children: [
72954
73020
  /* @__PURE__ */ jsx("div", { className: "px-5 py-4 flex-none flex justify-between items-center border-b border-slate-50/50 relative", children: /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-700", children: "Idle Time Breakdown" }) }),
72955
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 p-4 pt-2 relative", children: isIdleReasonBreakdownLoading ? /* @__PURE__ */ jsx(OverviewIdleBreakdownSkeleton, {}) : /* @__PURE__ */ jsx(
73021
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0 p-4 pt-2 relative", children: isIdleReasonBreakdownLoading ? /* @__PURE__ */ jsx(OverviewIdleBreakdownSkeleton, {}) : showIdleModuleNotEnabledState ? /* @__PURE__ */ jsx("div", { className: "h-full flex items-center justify-center rounded-xl border border-dashed border-slate-200 bg-slate-50/80 px-6 text-center", children: /* @__PURE__ */ jsxs("div", { children: [
73022
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-slate-700", children: "Module not enabled" }),
73023
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-slate-500", children: "Enable idle-time classification on at least one line to view this breakdown." })
73024
+ ] }) }) : /* @__PURE__ */ jsx(
72956
73025
  IdleTimeReasonChart,
72957
73026
  {
72958
73027
  data: idleBreakdown,
@@ -72981,7 +73050,8 @@ var PlantHeadView = () => {
72981
73050
  yAxisDomain: [0, 100],
72982
73051
  showLegend: false,
72983
73052
  showGrid: true,
72984
- fillContainer: true
73053
+ fillContainer: true,
73054
+ tooltipLabelFormatter: trendTooltipLabelFormatter
72985
73055
  },
72986
73056
  trendPlayKey
72987
73057
  ) }) })
@@ -72995,7 +73065,10 @@ var PlantHeadView = () => {
72995
73065
  "button",
72996
73066
  {
72997
73067
  type: "button",
72998
- onClick: () => navigate("/improvement-center"),
73068
+ onClick: () => {
73069
+ trackCoreEvent("Operations Overview View All Clicked", { section: "improvements" });
73070
+ navigate("/improvement-center");
73071
+ },
72999
73072
  className: "text-[11px] font-bold text-slate-500 hover:text-slate-800 transition-colors",
73000
73073
  children: "View All"
73001
73074
  }
@@ -73004,7 +73077,10 @@ var PlantHeadView = () => {
73004
73077
  /* @__PURE__ */ jsx("div", { className: "flex-1 p-0 flex flex-col px-5 py-2 justify-start overflow-auto", children: isImprovementsLoading ? /* @__PURE__ */ jsx(OverviewImprovementsSkeleton, {}) : improvements.length > 0 ? improvements.map((item) => /* @__PURE__ */ jsx(
73005
73078
  "div",
73006
73079
  {
73007
- onClick: () => navigate("/improvement-center"),
73080
+ onClick: () => {
73081
+ trackCoreEvent("Operations Overview Improvement Clicked", { issue_id: item.id, title: item.title });
73082
+ navigate("/improvement-center");
73083
+ },
73008
73084
  className: "flex items-center justify-between py-3 border-b border-slate-50 last:border-0 group cursor-pointer",
73009
73085
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 flex-1 min-w-0 pr-4", children: [
73010
73086
  /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx(ArrowUpRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-500 transition-colors" }) }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.11.3",
3
+ "version": "6.11.5",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",