@optifye/dashboard-core 6.12.26 → 6.12.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22075,14 +22075,87 @@ var toNumber = (value) => {
22075
22075
  }
22076
22076
  return 0;
22077
22077
  };
22078
+ var toOptionalNumber = (value) => {
22079
+ if (typeof value === "number" && Number.isFinite(value)) return value;
22080
+ if (typeof value === "string" && value.trim() !== "") {
22081
+ const parsed = Number(value);
22082
+ return Number.isFinite(parsed) ? parsed : null;
22083
+ }
22084
+ return null;
22085
+ };
22078
22086
  var EFFICIENCY_ON_TRACK_THRESHOLD = 100;
22079
22087
  var isEfficiencyOnTrack = (efficiency) => toNumber(efficiency) >= EFFICIENCY_ON_TRACK_THRESHOLD;
22088
+ var KPI_SIGNAL_LABELS = {
22089
+ stable: "Stable",
22090
+ warning: "Warning",
22091
+ attention: "Attention"
22092
+ };
22093
+ var normalizeLineSignalSource = (source) => source === "recent_flow" ? "recent_flow" : "efficiency";
22094
+ var normalizeLineSignal = (lineSignal, fallbackEfficiency, fallbackWeight) => {
22095
+ if (lineSignal && typeof lineSignal === "object") {
22096
+ const raw = lineSignal;
22097
+ return {
22098
+ source: normalizeLineSignalSource(raw.source),
22099
+ percent: toOptionalNumber(raw.percent),
22100
+ weight: toOptionalNumber(raw.weight),
22101
+ mode: raw.mode === "unavailable" ? "unavailable" : "computed",
22102
+ reason: typeof raw.reason === "string" ? raw.reason : raw.reason ?? null,
22103
+ effectiveEndAt: raw.effective_end_at ?? raw.effectiveEndAt ?? null,
22104
+ computedAt: raw.computed_at ?? raw.computedAt ?? null
22105
+ };
22106
+ }
22107
+ const percent2 = toOptionalNumber(fallbackEfficiency);
22108
+ if (percent2 === null) return null;
22109
+ return {
22110
+ source: "efficiency",
22111
+ percent: percent2,
22112
+ weight: toOptionalNumber(fallbackWeight),
22113
+ mode: "computed",
22114
+ reason: "fallback_efficiency",
22115
+ effectiveEndAt: null,
22116
+ computedAt: null
22117
+ };
22118
+ };
22119
+ var getKpiSignalStatus = (signal, legend = DEFAULT_EFFICIENCY_LEGEND) => {
22120
+ const percent2 = toOptionalNumber(signal?.percent);
22121
+ if (percent2 === null) return null;
22122
+ const color2 = getEfficiencyColor(percent2, legend);
22123
+ if (color2 === "green") return "stable";
22124
+ if (color2 === "yellow") return "warning";
22125
+ return "attention";
22126
+ };
22127
+ var getKpiSignalLabel = (signal, legend = DEFAULT_EFFICIENCY_LEGEND) => {
22128
+ const status = getKpiSignalStatus(signal, legend);
22129
+ return status ? KPI_SIGNAL_LABELS[status] : null;
22130
+ };
22131
+ var aggregateLineSignals = (signals) => {
22132
+ const numericSignals = signals.filter(
22133
+ (signal) => signal !== null && signal !== void 0 && toOptionalNumber(signal.percent) !== null
22134
+ );
22135
+ if (numericSignals.length === 0) return null;
22136
+ const percent2 = numericSignals.reduce(
22137
+ (sum, signal) => sum + (toOptionalNumber(signal.percent) ?? 0),
22138
+ 0
22139
+ ) / numericSignals.length;
22140
+ const signalSources = new Set(numericSignals.map((signal) => signal.source));
22141
+ const source = signalSources.size === 1 ? numericSignals[0].source : "mixed";
22142
+ return {
22143
+ source,
22144
+ percent: percent2,
22145
+ weight: null,
22146
+ mode: "computed",
22147
+ reason: "average",
22148
+ effectiveEndAt: null,
22149
+ computedAt: null
22150
+ };
22151
+ };
22080
22152
  var createDefaultKPIs = () => ({
22081
22153
  underperformingWorkers: { current: 0, total: 0, change: 0 },
22082
22154
  efficiency: { value: 0, change: 0 },
22083
22155
  outputProgress: { current: 0, target: 0, idealOutput: 0, change: 0 },
22084
22156
  avgCycleTime: { value: 0, change: 0 },
22085
- qualityCompliance: { value: 95, change: 0 }
22157
+ qualityCompliance: { value: 95, change: 0 },
22158
+ lineSignal: null
22086
22159
  });
22087
22160
  var buildKPIsFromLineMetricsRow = (row) => {
22088
22161
  if (!row) return createDefaultKPIs();
@@ -22116,15 +22189,28 @@ var buildKPIsFromLineMetricsRow = (row) => {
22116
22189
  qualityCompliance: {
22117
22190
  value: 95,
22118
22191
  change: 0
22119
- }
22192
+ },
22193
+ lineSignal: normalizeLineSignal(row.line_signal, avgEfficiency, idealOutput || lineThreshold)
22120
22194
  };
22121
22195
  };
22122
22196
  var aggregateKPIsFromLineMetricsRows = (rows) => {
22123
22197
  if (!rows || rows.length === 0) return createDefaultKPIs();
22198
+ const lineSignal = aggregateLineSignals(
22199
+ rows.map((row) => normalizeLineSignal(
22200
+ row?.line_signal,
22201
+ row?.avg_efficiency,
22202
+ row?.ideal_output ?? row?.line_threshold
22203
+ ))
22204
+ );
22124
22205
  const eligibleRows = rows.filter(
22125
22206
  (row) => isValidAggregateEfficiency(row?.monitoring_mode ?? row?.monitoringMode, row?.avg_efficiency)
22126
22207
  );
22127
- if (eligibleRows.length === 0) return createDefaultKPIs();
22208
+ if (eligibleRows.length === 0) {
22209
+ return {
22210
+ ...createDefaultKPIs(),
22211
+ lineSignal
22212
+ };
22213
+ }
22128
22214
  const currentOutputSum = eligibleRows.reduce((sum, row) => sum + toNumber(row.current_output), 0);
22129
22215
  const lineThresholdSum = eligibleRows.reduce(
22130
22216
  (sum, row) => sum + (row?.output_target_recalculated !== void 0 && row?.output_target_recalculated !== null ? toNumber(row.output_target_recalculated) : toNumber(row.line_threshold)),
@@ -22171,7 +22257,8 @@ var aggregateKPIsFromLineMetricsRows = (rows) => {
22171
22257
  qualityCompliance: {
22172
22258
  value: 95,
22173
22259
  change: 0
22174
- }
22260
+ },
22261
+ lineSignal
22175
22262
  };
22176
22263
  };
22177
22264
 
@@ -34725,19 +34812,19 @@ var SkuRow = ({ sku, isSelected, isLive, onSelect }) => {
34725
34812
  onClick: () => onSelect?.(isSelected ? null : sku.sku_id),
34726
34813
  "data-testid": `sku-progress-row-${sku.sku_id}`,
34727
34814
  children: [
34728
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [
34729
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
34815
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
34816
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2 flex-1 min-w-0", children: [
34730
34817
  /* @__PURE__ */ jsxRuntime.jsx(
34731
34818
  "span",
34732
34819
  {
34733
- className: "text-sm font-semibold text-gray-800 truncate",
34820
+ className: "text-xs sm:text-sm font-semibold text-gray-800 break-words line-clamp-2",
34734
34821
  title: skuLabel,
34735
34822
  children: skuLabel
34736
34823
  }
34737
34824
  ),
34738
- isLive && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-green-500 shrink-0 shadow-[0_0_4px_rgba(34,197,94,0.6)]", title: "Currently running" })
34825
+ isLive && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full bg-green-500 shrink-0 shadow-[0_0_4px_rgba(34,197,94,0.6)] mt-1.5", title: "Currently running" })
34739
34826
  ] }),
34740
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500 font-medium tabular-nums shrink-0", children: [
34827
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500 font-medium tabular-nums shrink-0 pt-0.5", children: [
34741
34828
  Math.round(current),
34742
34829
  " / ",
34743
34830
  Math.round(target)
@@ -45075,7 +45162,7 @@ var FileManagerFilters = ({
45075
45162
  onClick: () => {
45076
45163
  onIdleClipSortChange?.(idleClipSort === "latest" ? "idle_duration_desc" : "latest");
45077
45164
  },
45078
- className: `p-2 rounded-xl transition-all duration-200 ${idleClipSort === "idle_duration_desc" ? "bg-orange-100 text-orange-600 hover:bg-orange-200 shadow-sm" : "bg-slate-100 text-slate-600 hover:bg-slate-200"}`,
45165
+ className: `p-2 rounded-xl transition-all duration-200 ${idleClipSort === "idle_duration_desc" ? "bg-blue-100 text-blue-600 hover:bg-blue-200 shadow-sm" : "bg-slate-100 text-slate-600 hover:bg-slate-200"}`,
45079
45166
  title: idleClipSort === "idle_duration_desc" ? "Sort by newest first" : "Sort by longest idle first",
45080
45167
  "aria-label": idleClipSort === "idle_duration_desc" ? "Sort idle clips by newest first" : "Sort idle clips by longest idle first",
45081
45168
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownWideNarrow, { className: "h-5 w-5" })
@@ -45142,7 +45229,7 @@ var FileManagerFilters = ({
45142
45229
  }
45143
45230
  )
45144
45231
  ] }),
45145
- activeFilter === "idle_time" && idleClipSort === "idle_duration_desc" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-fit items-center gap-2 rounded-full border border-orange-100 bg-orange-50/70 px-2.5 py-1 text-xs text-orange-700 shadow-sm", children: [
45232
+ activeFilter === "idle_time" && idleClipSort === "idle_duration_desc" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex w-fit items-center gap-2 rounded-full border border-blue-100 bg-blue-50/70 px-2.5 py-1 text-xs text-blue-700 shadow-sm", children: [
45146
45233
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownWideNarrow, { className: "h-3.5 w-3.5" }),
45147
45234
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Longest idle first" }),
45148
45235
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -45152,7 +45239,7 @@ var FileManagerFilters = ({
45152
45239
  e.stopPropagation();
45153
45240
  onIdleClipSortChange?.("latest");
45154
45241
  },
45155
- className: "rounded-full p-0.5 transition-colors hover:bg-orange-100",
45242
+ className: "rounded-full p-0.5 transition-colors hover:bg-blue-100",
45156
45243
  title: "Clear idle sort",
45157
45244
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5" })
45158
45245
  }
@@ -57614,7 +57701,7 @@ var SideNavBar = React144.memo(({
57614
57701
  const role = user?.role_level;
57615
57702
  const roleNavPaths = React144.useMemo(() => getRoleNavPaths(role), [role]);
57616
57703
  const showLiveMonitorLink = roleNavPaths.includes("/live-monitor");
57617
- const rootDashboardSurface = React144.useMemo(() => role === "owner" || role === "plant_head" || role === "optifye" ? "operations_overview" : "monitor", [role]);
57704
+ const rootDashboardSurface = React144.useMemo(() => "monitor", []);
57618
57705
  const getBasePath = React144.useCallback((path) => {
57619
57706
  const firstSegment = path.split("?")[0].split("/").filter(Boolean)[0];
57620
57707
  return firstSegment ? `/${firstSegment}` : "/";
@@ -57665,12 +57752,6 @@ var SideNavBar = React144.memo(({
57665
57752
  dashboard_surface: dashboardSurface
57666
57753
  }
57667
57754
  }), []);
57668
- const handleHomeClick = React144.useCallback(() => {
57669
- navigate("/", {
57670
- trackingEvent: buildDashboardSurfaceTrackingEvent("side_nav", "/", rootDashboardSurface)
57671
- });
57672
- onMobileMenuClose?.();
57673
- }, [navigate, onMobileMenuClose, buildDashboardSurfaceTrackingEvent, rootDashboardSurface]);
57674
57755
  const handleLeaderboardClick = React144.useCallback(() => {
57675
57756
  navigate(`/leaderboard`, {
57676
57757
  trackingEvent: {
@@ -57976,7 +58057,6 @@ var SideNavBar = React144.memo(({
57976
58057
  });
57977
58058
  onMobileMenuClose?.();
57978
58059
  }, [navigate, onMobileMenuClose, buildDashboardSurfaceTrackingEvent, rootDashboardSurface]);
57979
- const homeButtonClasses = React144.useMemo(() => getButtonClasses("/"), [getButtonClasses]);
57980
58060
  const liveButtonClasses = React144.useMemo(() => getButtonClasses("/live-monitor"), [getButtonClasses]);
57981
58061
  const leaderboardButtonClasses = React144.useMemo(() => getButtonClasses("/leaderboard"), [getButtonClasses]);
57982
58062
  const kpisButtonClasses = React144.useMemo(() => getButtonClasses("/kpis"), [getButtonClasses]);
@@ -58007,118 +58087,99 @@ var SideNavBar = React144.memo(({
58007
58087
  children: /* @__PURE__ */ jsxRuntime.jsx(Logo, { className: "w-12 h-12 object-contain cursor-pointer" })
58008
58088
  }
58009
58089
  ) }),
58010
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 w-full py-6 px-4 overflow-y-auto", children: [
58011
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
58012
- canAccessPath("/") && /* @__PURE__ */ jsxRuntime.jsxs(
58013
- "button",
58014
- {
58015
- onClick: handleHomeClick,
58016
- className: homeButtonClasses,
58017
- "aria-label": "Home",
58018
- tabIndex: 0,
58019
- role: "tab",
58020
- "aria-selected": isPathActive("/"),
58021
- children: [
58022
- /* @__PURE__ */ jsxRuntime.jsx(outline.HomeIcon, { className: "w-5 h-5 mb-1" }),
58023
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Home" })
58024
- ]
58025
- }
58026
- ),
58027
- showLiveMonitorLink && canAccessPath("/live-monitor") && /* @__PURE__ */ jsxRuntime.jsxs(
58028
- "button",
58029
- {
58030
- onClick: handleLiveClick,
58031
- className: `${liveButtonClasses} mt-3`,
58032
- "aria-label": "Monitor",
58033
- tabIndex: 0,
58034
- role: "tab",
58035
- "aria-selected": isPathActive("/live-monitor"),
58036
- children: [
58037
- /* @__PURE__ */ jsxRuntime.jsx(outline.VideoCameraIcon, { className: "w-5 h-5 mb-1" }),
58038
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Monitor" })
58039
- ]
58040
- }
58041
- )
58042
- ] }),
58043
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
58044
- canAccessPath("/leaderboard") && /* @__PURE__ */ jsxRuntime.jsxs(
58045
- "button",
58046
- {
58047
- onClick: handleLeaderboardClick,
58048
- className: leaderboardButtonClasses,
58049
- "aria-label": "Leaderboard",
58050
- tabIndex: 0,
58051
- role: "tab",
58052
- "aria-selected": isPathActive("/leaderboard"),
58053
- children: [
58054
- /* @__PURE__ */ jsxRuntime.jsx(outline.TrophyIcon, { className: "w-5 h-5 mb-1" }),
58055
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Leaders" })
58056
- ]
58057
- }
58058
- ),
58059
- canAccessPath("/kpis") && /* @__PURE__ */ jsxRuntime.jsxs(
58060
- "button",
58061
- {
58062
- onClick: handleKPIsClick,
58063
- className: kpisButtonClasses,
58064
- "aria-label": "Lines",
58065
- tabIndex: 0,
58066
- role: "tab",
58067
- "aria-selected": isPathActive("/kpis"),
58068
- children: [
58069
- /* @__PURE__ */ jsxRuntime.jsx(outline.ChartBarIcon, { className: "w-5 h-5 mb-1" }),
58070
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Lines" })
58071
- ]
58072
- }
58073
- ),
58074
- canAccessPath("/improvement-center") && /* @__PURE__ */ jsxRuntime.jsxs(
58075
- "button",
58076
- {
58077
- onClick: handleImprovementClick,
58078
- className: improvementButtonClasses,
58079
- "aria-label": "Improvement Center",
58080
- tabIndex: 0,
58081
- role: "tab",
58082
- "aria-selected": isPathActive("/improvement-center"),
58083
- children: [
58084
- /* @__PURE__ */ jsxRuntime.jsx(outline.LightBulbIcon, { className: "w-5 h-5 mb-1" }),
58085
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight text-center", children: "Improve" })
58086
- ]
58087
- }
58088
- ),
58089
- showSupervisorManagement,
58090
- skuEnabled && canAccessPath("/skus") && /* @__PURE__ */ jsxRuntime.jsxs(
58091
- "button",
58092
- {
58093
- onClick: handleSKUsClick,
58094
- className: skusButtonClasses,
58095
- "aria-label": "SKU Management",
58096
- tabIndex: 0,
58097
- role: "tab",
58098
- "aria-selected": isPathActive("/skus"),
58099
- children: [
58100
- /* @__PURE__ */ jsxRuntime.jsx(outline.CubeIcon, { className: "w-5 h-5 mb-1" }),
58101
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "SKUs" })
58102
- ]
58103
- }
58104
- ),
58105
- canAccessPath("/health") && /* @__PURE__ */ jsxRuntime.jsxs(
58106
- "button",
58107
- {
58108
- onClick: handleHealthClick,
58109
- className: healthButtonClasses,
58110
- "aria-label": "System Health",
58111
- tabIndex: 0,
58112
- role: "tab",
58113
- "aria-selected": isPathActive("/health"),
58114
- children: [
58115
- /* @__PURE__ */ jsxRuntime.jsx(outline.HeartIcon, { className: "w-5 h-5 mb-1" }),
58116
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Health" })
58117
- ]
58118
- }
58119
- )
58120
- ] })
58121
- ] }),
58090
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full py-6 px-4 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
58091
+ showLiveMonitorLink && canAccessPath("/live-monitor") && /* @__PURE__ */ jsxRuntime.jsxs(
58092
+ "button",
58093
+ {
58094
+ onClick: handleLiveClick,
58095
+ className: liveButtonClasses,
58096
+ "aria-label": "Monitor",
58097
+ tabIndex: 0,
58098
+ role: "tab",
58099
+ "aria-selected": isPathActive("/live-monitor"),
58100
+ children: [
58101
+ /* @__PURE__ */ jsxRuntime.jsx(outline.VideoCameraIcon, { className: "w-5 h-5 mb-1" }),
58102
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Monitor" })
58103
+ ]
58104
+ }
58105
+ ),
58106
+ canAccessPath("/leaderboard") && /* @__PURE__ */ jsxRuntime.jsxs(
58107
+ "button",
58108
+ {
58109
+ onClick: handleLeaderboardClick,
58110
+ className: leaderboardButtonClasses,
58111
+ "aria-label": "Leaderboard",
58112
+ tabIndex: 0,
58113
+ role: "tab",
58114
+ "aria-selected": isPathActive("/leaderboard"),
58115
+ children: [
58116
+ /* @__PURE__ */ jsxRuntime.jsx(outline.TrophyIcon, { className: "w-5 h-5 mb-1" }),
58117
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Leaders" })
58118
+ ]
58119
+ }
58120
+ ),
58121
+ canAccessPath("/kpis") && /* @__PURE__ */ jsxRuntime.jsxs(
58122
+ "button",
58123
+ {
58124
+ onClick: handleKPIsClick,
58125
+ className: kpisButtonClasses,
58126
+ "aria-label": "Lines",
58127
+ tabIndex: 0,
58128
+ role: "tab",
58129
+ "aria-selected": isPathActive("/kpis"),
58130
+ children: [
58131
+ /* @__PURE__ */ jsxRuntime.jsx(outline.ChartBarIcon, { className: "w-5 h-5 mb-1" }),
58132
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Lines" })
58133
+ ]
58134
+ }
58135
+ ),
58136
+ canAccessPath("/improvement-center") && /* @__PURE__ */ jsxRuntime.jsxs(
58137
+ "button",
58138
+ {
58139
+ onClick: handleImprovementClick,
58140
+ className: improvementButtonClasses,
58141
+ "aria-label": "Improvement Center",
58142
+ tabIndex: 0,
58143
+ role: "tab",
58144
+ "aria-selected": isPathActive("/improvement-center"),
58145
+ children: [
58146
+ /* @__PURE__ */ jsxRuntime.jsx(outline.LightBulbIcon, { className: "w-5 h-5 mb-1" }),
58147
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight text-center", children: "Improve" })
58148
+ ]
58149
+ }
58150
+ ),
58151
+ showSupervisorManagement,
58152
+ skuEnabled && canAccessPath("/skus") && /* @__PURE__ */ jsxRuntime.jsxs(
58153
+ "button",
58154
+ {
58155
+ onClick: handleSKUsClick,
58156
+ className: skusButtonClasses,
58157
+ "aria-label": "SKU Management",
58158
+ tabIndex: 0,
58159
+ role: "tab",
58160
+ "aria-selected": isPathActive("/skus"),
58161
+ children: [
58162
+ /* @__PURE__ */ jsxRuntime.jsx(outline.CubeIcon, { className: "w-5 h-5 mb-1" }),
58163
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "SKUs" })
58164
+ ]
58165
+ }
58166
+ ),
58167
+ canAccessPath("/health") && /* @__PURE__ */ jsxRuntime.jsxs(
58168
+ "button",
58169
+ {
58170
+ onClick: handleHealthClick,
58171
+ className: healthButtonClasses,
58172
+ "aria-label": "System Health",
58173
+ tabIndex: 0,
58174
+ role: "tab",
58175
+ "aria-selected": isPathActive("/health"),
58176
+ children: [
58177
+ /* @__PURE__ */ jsxRuntime.jsx(outline.HeartIcon, { className: "w-5 h-5 mb-1" }),
58178
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs sm:text-[10px] font-medium leading-tight", children: "Health" })
58179
+ ]
58180
+ }
58181
+ )
58182
+ ] }) }),
58122
58183
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full py-4 px-4 border-t border-gray-100 flex-shrink-0 flex flex-col gap-2", children: settingsItems.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
58123
58184
  "button",
58124
58185
  {
@@ -58155,23 +58216,11 @@ var SideNavBar = React144.memo(({
58155
58216
  };
58156
58217
  };
58157
58218
  return /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "px-5 py-6", children: [
58158
- canAccessPath("/") && /* @__PURE__ */ jsxRuntime.jsxs(
58159
- "button",
58160
- {
58161
- onClick: handleMobileNavClick(handleHomeClick),
58162
- className: getMobileButtonClass("/"),
58163
- "aria-label": "Home",
58164
- children: [
58165
- /* @__PURE__ */ jsxRuntime.jsx(outline.HomeIcon, { className: getIconClass("/") }),
58166
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-medium", children: "Home" })
58167
- ]
58168
- }
58169
- ),
58170
58219
  showLiveMonitorLink && canAccessPath("/live-monitor") && /* @__PURE__ */ jsxRuntime.jsxs(
58171
58220
  "button",
58172
58221
  {
58173
58222
  onClick: handleMobileNavClick(handleLiveClick),
58174
- className: `${getMobileButtonClass("/live-monitor")} mt-2`,
58223
+ className: getMobileButtonClass("/live-monitor"),
58175
58224
  "aria-label": "Monitor",
58176
58225
  children: [
58177
58226
  /* @__PURE__ */ jsxRuntime.jsx(outline.VideoCameraIcon, { className: getIconClass("/live-monitor") }),
@@ -69737,6 +69786,19 @@ var formatDateKey2 = (dateKey, timezone, options) => {
69737
69786
  return dateKey;
69738
69787
  }
69739
69788
  };
69789
+ var getFirstName = (displayName) => {
69790
+ const trimmedName = displayName.trim();
69791
+ if (!trimmedName) return "";
69792
+ return trimmedName.split(/\s+/)[0];
69793
+ };
69794
+ var formatSupervisorFirstNames = (supervisors, fallbackSupervisorNames) => {
69795
+ const firstNames = supervisors.map((supervisor) => getFirstName(supervisor.displayName)).filter((name) => name.length > 0);
69796
+ if (firstNames.length > 0) {
69797
+ return Array.from(new Set(firstNames)).join(", ");
69798
+ }
69799
+ const fallbackFirstNames = fallbackSupervisorNames.flatMap((names) => names.split(",")).map(getFirstName).filter((name) => name.length > 0);
69800
+ return fallbackFirstNames.length > 0 ? Array.from(new Set(fallbackFirstNames)).join(", ") : "Unassigned";
69801
+ };
69740
69802
  var LeaderboardCountdown = ({ targetDate, format: format10, finishedLabel = "Finished", placeholder = "--", onFinished }) => {
69741
69803
  const [time2, setTime] = React144.useState("");
69742
69804
  const hasFinishedRef = React144.useRef(false);
@@ -69862,7 +69924,7 @@ var LinesLeaderboard = ({
69862
69924
  });
69863
69925
  const supervisors = Array.from(supervisorByUserId.values());
69864
69926
  const primarySupervisor = supervisors[0];
69865
- const supervisorName = supervisors.length > 1 ? `${supervisors.length} supervisors` : primarySupervisor?.displayName || fallbackSupervisorNames[0] || "Unassigned";
69927
+ const supervisorName = formatSupervisorFirstNames(supervisors, fallbackSupervisorNames);
69866
69928
  const supervisorImage = primarySupervisor?.profilePhotoUrl || null;
69867
69929
  return {
69868
69930
  ...row,
@@ -70122,21 +70184,49 @@ var LinesLeaderboard = ({
70122
70184
  ] }) }) }) })
70123
70185
  ] });
70124
70186
  };
70187
+ var SIGNAL_PILL_STYLES = {
70188
+ stable: {
70189
+ container: "bg-emerald-100 text-emerald-700 border border-emerald-200",
70190
+ dot: "bg-emerald-500"
70191
+ },
70192
+ warning: {
70193
+ container: "bg-amber-100 text-amber-700 border border-amber-200",
70194
+ dot: "bg-amber-500"
70195
+ },
70196
+ attention: {
70197
+ container: "bg-red-100 text-red-700 border border-red-200",
70198
+ dot: "bg-red-500"
70199
+ }
70200
+ };
70201
+ var KpiSignalPill = ({ signal, efficiencyLegend }) => {
70202
+ const status = getKpiSignalStatus(signal, efficiencyLegend);
70203
+ const label = getKpiSignalLabel(signal, efficiencyLegend);
70204
+ if (!status || !label) return null;
70205
+ const styles2 = SIGNAL_PILL_STYLES[status];
70206
+ return /* @__PURE__ */ jsxRuntime.jsxs(
70207
+ "div",
70208
+ {
70209
+ className: `flex items-center gap-1.5 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full text-xs font-medium flex-shrink-0 ${styles2.container}`,
70210
+ style: { minWidth: "fit-content" },
70211
+ children: [
70212
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${styles2.dot} animate-pulse` }),
70213
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
70214
+ ]
70215
+ }
70216
+ );
70217
+ };
70125
70218
  var LineCard = ({
70126
70219
  line,
70127
70220
  kpis,
70128
70221
  isLoading,
70129
70222
  error,
70130
70223
  onClick,
70224
+ efficiencyLegend,
70131
70225
  supervisorEnabled = false,
70132
70226
  supervisorName,
70133
70227
  supervisors
70134
70228
  }) => {
70135
70229
  const isUptimeLine = (line.monitoring_mode ?? "output") === "uptime";
70136
- const isOnTrack = React144__namespace.default.useMemo(() => {
70137
- if (!kpis) return null;
70138
- return isEfficiencyOnTrack(kpis.efficiency.value);
70139
- }, [kpis]);
70140
70230
  return /* @__PURE__ */ jsxRuntime.jsxs(
70141
70231
  motion.div,
70142
70232
  {
@@ -70208,10 +70298,7 @@ var LineCard = ({
70208
70298
  ] })
70209
70299
  ] })
70210
70300
  ] }),
70211
- !isUptimeLine && kpis && isOnTrack !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1.5 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full text-xs font-medium flex-shrink-0 ${isOnTrack ? "bg-emerald-100 text-emerald-700 border border-emerald-200" : "bg-red-100 text-red-700 border border-red-200"}`, style: { minWidth: "fit-content" }, children: [
70212
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
70213
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: isOnTrack ? "On Track" : "Behind" })
70214
- ] })
70301
+ !isUptimeLine && kpis && /* @__PURE__ */ jsxRuntime.jsx(KpiSignalPill, { signal: kpis.lineSignal, efficiencyLegend })
70215
70302
  ] }) }),
70216
70303
  isLoading && !kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
70217
70304
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
@@ -70279,12 +70366,9 @@ var KpiGroupCard = ({
70279
70366
  isLoading,
70280
70367
  error,
70281
70368
  isUptimeMode,
70369
+ efficiencyLegend,
70282
70370
  onClick
70283
70371
  }) => {
70284
- const isOnTrack = React144__namespace.default.useMemo(() => {
70285
- if (!kpis) return null;
70286
- return isEfficiencyOnTrack(kpis.efficiency.value);
70287
- }, [kpis]);
70288
70372
  const outputTarget = Number(kpis?.outputProgress?.target ?? 0);
70289
70373
  const outputCurrent = Number(kpis?.outputProgress?.current ?? 0);
70290
70374
  const progressPercent = outputTarget > 0 ? Math.min(outputCurrent / outputTarget * 100, 100) : 0;
@@ -70308,10 +70392,7 @@ var KpiGroupCard = ({
70308
70392
  ),
70309
70393
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs font-medium text-gray-500", children: subtitle })
70310
70394
  ] }),
70311
- !isUptimeMode && kpis && isOnTrack !== null && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center gap-1.5 px-2.5 sm:px-3 py-1 sm:py-1.5 rounded-full text-xs font-medium flex-shrink-0 ${isOnTrack ? "bg-emerald-100 text-emerald-700 border border-emerald-200" : "bg-red-100 text-red-700 border border-red-200"}`, style: { minWidth: "fit-content" }, children: [
70312
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-2 h-2 rounded-full ${isOnTrack ? "bg-emerald-500" : "bg-red-500"} animate-pulse` }),
70313
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: isOnTrack ? "On Track" : "Behind" })
70314
- ] })
70395
+ !isUptimeMode && kpis && /* @__PURE__ */ jsxRuntime.jsx(KpiSignalPill, { signal: kpis.lineSignal, efficiencyLegend })
70315
70396
  ] }) }),
70316
70397
  isLoading && !kpis && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
70317
70398
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "animate-pulse", children: [
@@ -70628,7 +70709,8 @@ var KPIsOverviewView = ({
70628
70709
  const {
70629
70710
  lineMetrics,
70630
70711
  isLoading: metricsLoading,
70631
- error: metricsError
70712
+ error: metricsError,
70713
+ efficiencyLegend
70632
70714
  } = useDashboardMetrics({
70633
70715
  lineId: factoryViewId,
70634
70716
  userAccessibleLineIds: metricsLineIds
@@ -70972,7 +71054,11 @@ var KPIsOverviewView = ({
70972
71054
  trackProps.output_current = kpis.outputProgress?.current;
70973
71055
  trackProps.output_target = kpis.outputProgress?.target;
70974
71056
  trackProps.underperforming_workers = kpis.underperformingWorkers?.current;
70975
- trackProps.status = isEfficiencyOnTrack(kpis.efficiency?.value) ? "On Track" : "Behind";
71057
+ const signalLabel = getKpiSignalLabel(kpis.lineSignal, efficiencyLegend);
71058
+ if (signalLabel) {
71059
+ trackProps.status = signalLabel;
71060
+ trackProps.signal_source = kpis.lineSignal?.source ?? null;
71061
+ }
70976
71062
  }
70977
71063
  trackCoreEvent("Line Card Clicked", trackProps);
70978
71064
  if (activeTab === "leaderboard" && timeRange === "today" && isHistoricalLeaderboardDaily) {
@@ -71058,6 +71144,7 @@ var KPIsOverviewView = ({
71058
71144
  isLoading: metricsLoading,
71059
71145
  error: metricsError,
71060
71146
  onClick: (kpis) => handleLineClick(line, kpis),
71147
+ efficiencyLegend,
71061
71148
  supervisorEnabled,
71062
71149
  supervisorName: supervisorNamesByLineId.get(line.id) || null,
71063
71150
  supervisors: supervisorsByLineId?.get(line.id)
@@ -71079,6 +71166,7 @@ var KPIsOverviewView = ({
71079
71166
  isLoading: metricsLoading,
71080
71167
  error: metricsError,
71081
71168
  isUptimeMode: viewType === "machine",
71169
+ efficiencyLegend,
71082
71170
  onClick
71083
71171
  },
71084
71172
  key
@@ -87265,6 +87353,7 @@ exports.KPIDetailView = KPIDetailView_default;
87265
87353
  exports.KPIGrid = KPIGrid;
87266
87354
  exports.KPIHeader = KPIHeader;
87267
87355
  exports.KPISection = KPISection;
87356
+ exports.KPI_SIGNAL_LABELS = KPI_SIGNAL_LABELS;
87268
87357
  exports.KPIsOverviewView = KPIsOverviewView_default;
87269
87358
  exports.LINE_1_UUID = LINE_1_UUID;
87270
87359
  exports.LINE_2_UUID = LINE_2_UUID;
@@ -87398,6 +87487,7 @@ exports.WorkspaceWhatsAppShareButton = WorkspaceWhatsAppShareButton;
87398
87487
  exports.actionService = actionService;
87399
87488
  exports.addSentryBreadcrumb = addSentryBreadcrumb;
87400
87489
  exports.aggregateKPIsFromLineMetricsRows = aggregateKPIsFromLineMetricsRows;
87490
+ exports.aggregateLineSignals = aggregateLineSignals;
87401
87491
  exports.alertsService = alertsService;
87402
87492
  exports.apiUtils = apiUtils;
87403
87493
  exports.areAllLinesOnSameShift = areAllLinesOnSameShift;
@@ -87502,6 +87592,8 @@ exports.getDefaultCameraStreamUrl = getDefaultCameraStreamUrl;
87502
87592
  exports.getDefaultLineId = getDefaultLineId;
87503
87593
  exports.getDefaultTabForWorkspace = getDefaultTabForWorkspace;
87504
87594
  exports.getInitials = getInitials;
87595
+ exports.getKpiSignalLabel = getKpiSignalLabel;
87596
+ exports.getKpiSignalStatus = getKpiSignalStatus;
87505
87597
  exports.getLineDisplayName = getLineDisplayName;
87506
87598
  exports.getManufacturingInsights = getManufacturingInsights;
87507
87599
  exports.getMetricsTablePrefix = getMetricsTablePrefix;