@optifye/dashboard-core 6.12.29 → 6.12.31

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
@@ -538,6 +538,9 @@ body {
538
538
  .left-1 {
539
539
  left: 0.25rem;
540
540
  }
541
+ .left-1\.5 {
542
+ left: 0.375rem;
543
+ }
541
544
  .left-1\/2 {
542
545
  left: 50%;
543
546
  }
@@ -595,6 +598,9 @@ body {
595
598
  .top-1 {
596
599
  top: 0.25rem;
597
600
  }
601
+ .top-1\.5 {
602
+ top: 0.375rem;
603
+ }
598
604
  .top-1\/2 {
599
605
  top: 50%;
600
606
  }
@@ -1740,6 +1746,10 @@ body {
1740
1746
  --tw-rotate: 180deg;
1741
1747
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1742
1748
  }
1749
+ .rotate-45 {
1750
+ --tw-rotate: 45deg;
1751
+ transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1752
+ }
1743
1753
  .scale-0 {
1744
1754
  --tw-scale-x: 0;
1745
1755
  --tw-scale-y: 0;
@@ -2261,6 +2271,9 @@ body {
2261
2271
  .border-b-\[6px\] {
2262
2272
  border-bottom-width: 6px;
2263
2273
  }
2274
+ .border-l {
2275
+ border-left-width: 1px;
2276
+ }
2264
2277
  .border-l-2 {
2265
2278
  border-left-width: 2px;
2266
2279
  }
@@ -3091,6 +3104,12 @@ body {
3091
3104
  .bg-slate-900\/60 {
3092
3105
  background-color: rgb(15 23 42 / 0.6);
3093
3106
  }
3107
+ .bg-slate-950\/70 {
3108
+ background-color: rgb(2 6 23 / 0.7);
3109
+ }
3110
+ .bg-slate-950\/95 {
3111
+ background-color: rgb(2 6 23 / 0.95);
3112
+ }
3094
3113
  .bg-teal-100 {
3095
3114
  --tw-bg-opacity: 1;
3096
3115
  background-color: rgb(204 251 241 / var(--tw-bg-opacity, 1));
@@ -4974,6 +4993,22 @@ body {
4974
4993
  var(--tw-ring-shadow, 0 0 #0000),
4975
4994
  var(--tw-shadow);
4976
4995
  }
4996
+ .shadow-\[0_3px_10px_rgba\(0\,0\,0\,0\.34\)\,inset_0_0_0_1px_rgba\(255\,255\,255\,0\.18\)\] {
4997
+ --tw-shadow: 0 3px 10px rgba(0,0,0,0.34),inset 0 0 0 1px rgba(255,255,255,0.18);
4998
+ --tw-shadow-colored: 0 3px 10px var(--tw-shadow-color), inset 0 0 0 1px var(--tw-shadow-color);
4999
+ box-shadow:
5000
+ var(--tw-ring-offset-shadow, 0 0 #0000),
5001
+ var(--tw-ring-shadow, 0 0 #0000),
5002
+ var(--tw-shadow);
5003
+ }
5004
+ .shadow-\[0_6px_18px_rgba\(0\,0\,0\,0\.34\)\] {
5005
+ --tw-shadow: 0 6px 18px rgba(0,0,0,0.34);
5006
+ --tw-shadow-colored: 0 6px 18px var(--tw-shadow-color);
5007
+ box-shadow:
5008
+ var(--tw-ring-offset-shadow, 0 0 #0000),
5009
+ var(--tw-ring-shadow, 0 0 #0000),
5010
+ var(--tw-shadow);
5011
+ }
4977
5012
  .shadow-inner {
4978
5013
  --tw-shadow: inset 0 2px 4px 0 rgb(0 0 0 / 0.05);
4979
5014
  --tw-shadow-colored: inset 0 2px 4px 0 var(--tw-shadow-color);
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LineSignal, V as VideoGridMetricMode, W as WorkspaceMetrics, a as LineInfo, b as WorkspaceDetailedMetrics, S as SkuBreakdownItem, c as SkuSegmentItem, d as LineSkuBreakdownItem, E as EfficiencyLegendUpdate, D as DashboardKPIs, P as PoorPerformingWorkspace, e as WorkspaceVideoStream, K as KpiTrend, M as MonthlyTrendSummary, f as Workspace, g as WorkspaceCameraIpInfo, h as WorkspaceActionUpdate, A as ActionThreshold, i as ShiftConfiguration, j as KpiSignal, k as LineIssueResolutionSummary } from './automation-DYa3jNEf.mjs';
2
- export { r as CountDelta, C as CurrentWorkspaceSKU, s as DurationDelta, o as KpiSignalMode, n as KpiSignalSource, l as LineDetailedMetrics, m as LineSignalSource, u as LineThreshold, q as PercentageDelta, z as RecentFlowSnapshotGrid, R as RecentFlowSnapshotGridProps, v as ShiftConfigurationRecord, p as ValueDelta, t as WorkspaceCropRect, x as isRecentFlowVideoGridMetricMode, y as isWipGatedVideoGridMetricMode, w as normalizeVideoGridMetricMode } from './automation-DYa3jNEf.mjs';
1
+ import { L as LineSignal, V as VideoGridMetricMode, W as WorkspaceMetrics, a as LineInfo, b as WorkspaceDetailedMetrics, S as SkuBreakdownItem, c as SkuSegmentItem, d as LineSkuBreakdownItem, E as EfficiencyLegendUpdate, D as DashboardKPIs, P as PoorPerformingWorkspace, e as WorkspaceVideoStream, K as KpiTrend, M as MonthlyTrendSummary, f as Workspace, g as WorkspaceCameraIpInfo, h as WorkspaceActionUpdate, A as ActionThreshold, i as ShiftConfiguration, j as KpiSignal, k as LineIssueResolutionSummary } from './automation-ZIumB5W9.mjs';
2
+ export { s as CountDelta, C as CurrentWorkspaceSKU, t as DurationDelta, p as KpiSignalMode, o as KpiSignalSource, l as LineDetailedMetrics, n as LineSignalSource, v as LineThreshold, r as PercentageDelta, B as RecentFlowSnapshotGrid, R as RecentFlowSnapshotGridProps, w as ShiftConfigurationRecord, q as ValueDelta, m as VideoGridStatusBadge, u as WorkspaceCropRect, y as isRecentFlowVideoGridMetricMode, z as isWipGatedVideoGridMetricMode, x as normalizeVideoGridMetricMode } from './automation-ZIumB5W9.mjs';
3
3
  import * as _supabase_supabase_js from '@supabase/supabase-js';
4
4
  import { SupabaseClient as SupabaseClient$1, Session, User, AuthError } from '@supabase/supabase-js';
5
5
  import { LucideProps, Share2, Download } from 'lucide-react';
@@ -5262,9 +5262,11 @@ declare const pickPreferredLineMetricsRow: (supabase: {
5262
5262
  * - Aggregates `current_output` and `ideal_output` sum across
5263
5263
  * **real-SKU rows only**. Dummy rows contribute 0 — they are skipped
5264
5264
  * from that summation set.
5265
- * - Weighted-average fields (avg_efficiency, avg_cycle_time, threshold_pph)
5266
- * use per-row active-minutes as weight; falls back to plain mean when
5267
- * no row has positive weight.
5265
+ * - Weighted-average fields (avg_efficiency, avg_cycle_time) use per-row
5266
+ * active-minutes as weight; falls back to plain mean when no row has
5267
+ * positive weight.
5268
+ * - `threshold_pph` sums across real rows so it matches the Targets
5269
+ * page/API additive output-family PPH rule.
5268
5270
  * - `underperforming_workspaces` is averaged across ACTIVE real rows only
5269
5271
  * (real row with `current_output > 0 || ideal_output > 0`), rounded
5270
5272
  * half-up to the nearest whole number. Names / UUIDs are deduped unions
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { L as LineSignal, V as VideoGridMetricMode, W as WorkspaceMetrics, a as LineInfo, b as WorkspaceDetailedMetrics, S as SkuBreakdownItem, c as SkuSegmentItem, d as LineSkuBreakdownItem, E as EfficiencyLegendUpdate, D as DashboardKPIs, P as PoorPerformingWorkspace, e as WorkspaceVideoStream, K as KpiTrend, M as MonthlyTrendSummary, f as Workspace, g as WorkspaceCameraIpInfo, h as WorkspaceActionUpdate, A as ActionThreshold, i as ShiftConfiguration, j as KpiSignal, k as LineIssueResolutionSummary } from './automation-DYa3jNEf.js';
2
- export { r as CountDelta, C as CurrentWorkspaceSKU, s as DurationDelta, o as KpiSignalMode, n as KpiSignalSource, l as LineDetailedMetrics, m as LineSignalSource, u as LineThreshold, q as PercentageDelta, z as RecentFlowSnapshotGrid, R as RecentFlowSnapshotGridProps, v as ShiftConfigurationRecord, p as ValueDelta, t as WorkspaceCropRect, x as isRecentFlowVideoGridMetricMode, y as isWipGatedVideoGridMetricMode, w as normalizeVideoGridMetricMode } from './automation-DYa3jNEf.js';
1
+ import { L as LineSignal, V as VideoGridMetricMode, W as WorkspaceMetrics, a as LineInfo, b as WorkspaceDetailedMetrics, S as SkuBreakdownItem, c as SkuSegmentItem, d as LineSkuBreakdownItem, E as EfficiencyLegendUpdate, D as DashboardKPIs, P as PoorPerformingWorkspace, e as WorkspaceVideoStream, K as KpiTrend, M as MonthlyTrendSummary, f as Workspace, g as WorkspaceCameraIpInfo, h as WorkspaceActionUpdate, A as ActionThreshold, i as ShiftConfiguration, j as KpiSignal, k as LineIssueResolutionSummary } from './automation-ZIumB5W9.js';
2
+ export { s as CountDelta, C as CurrentWorkspaceSKU, t as DurationDelta, p as KpiSignalMode, o as KpiSignalSource, l as LineDetailedMetrics, n as LineSignalSource, v as LineThreshold, r as PercentageDelta, B as RecentFlowSnapshotGrid, R as RecentFlowSnapshotGridProps, w as ShiftConfigurationRecord, q as ValueDelta, m as VideoGridStatusBadge, u as WorkspaceCropRect, y as isRecentFlowVideoGridMetricMode, z as isWipGatedVideoGridMetricMode, x as normalizeVideoGridMetricMode } from './automation-ZIumB5W9.js';
3
3
  import * as _supabase_supabase_js from '@supabase/supabase-js';
4
4
  import { SupabaseClient as SupabaseClient$1, Session, User, AuthError } from '@supabase/supabase-js';
5
5
  import { LucideProps, Share2, Download } from 'lucide-react';
@@ -5262,9 +5262,11 @@ declare const pickPreferredLineMetricsRow: (supabase: {
5262
5262
  * - Aggregates `current_output` and `ideal_output` sum across
5263
5263
  * **real-SKU rows only**. Dummy rows contribute 0 — they are skipped
5264
5264
  * from that summation set.
5265
- * - Weighted-average fields (avg_efficiency, avg_cycle_time, threshold_pph)
5266
- * use per-row active-minutes as weight; falls back to plain mean when
5267
- * no row has positive weight.
5265
+ * - Weighted-average fields (avg_efficiency, avg_cycle_time) use per-row
5266
+ * active-minutes as weight; falls back to plain mean when no row has
5267
+ * positive weight.
5268
+ * - `threshold_pph` sums across real rows so it matches the Targets
5269
+ * page/API additive output-family PPH rule.
5268
5270
  * - `underperforming_workspaces` is averaged across ACTIVE real rows only
5269
5271
  * (real row with `current_output > 0 || ideal_output > 0`), rounded
5270
5272
  * half-up to the nearest whole number. Names / UUIDs are deduped unions
package/dist/index.js CHANGED
@@ -4665,7 +4665,10 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
4665
4665
  };
4666
4666
  const avgEfficiency = weighted("avg_efficiency");
4667
4667
  const avgCycleTime = weighted("avg_cycle_time");
4668
- const thresholdPph = weighted("threshold_pph");
4668
+ const thresholdPph = rowsForAggregation.reduce(
4669
+ (acc, row) => acc + (coerceOptionalNumber(row.threshold_pph) ?? 0),
4670
+ 0
4671
+ );
4669
4672
  const underperformingWorkspaces = activeRealRows.length > 0 ? roundHalfUpInt(
4670
4673
  activeRealRows.reduce(
4671
4674
  (acc, row) => acc + safeInt(row.underperforming_workspaces),
@@ -14716,6 +14719,7 @@ var transformMonitorWorkspaceMetrics = ({
14716
14719
  monitoring_mode: item.monitoring_mode ?? void 0,
14717
14720
  idle_time: idleTimeSeconds,
14718
14721
  idle_time_hourly: item.idle_time_hourly ?? null,
14722
+ idle_reason_hourly: item.idle_reason_hourly ?? null,
14719
14723
  shift_start: item.shift_start ?? void 0,
14720
14724
  shift_end: item.shift_end ?? void 0,
14721
14725
  assembly_enabled: item.assembly_enabled ?? false,
@@ -14747,10 +14751,12 @@ var transformMonitorWorkspaceMetrics = ({
14747
14751
  video_grid_green_streak_minutes: item.video_grid_green_streak_minutes ?? null,
14748
14752
  video_grid_green_streak_anchor_at: item.video_grid_green_streak_anchor_at ?? null,
14749
14753
  video_grid_green_streak_started_at: item.video_grid_green_streak_started_at ?? null,
14754
+ video_grid_green_streak_observed_at: item.video_grid_green_streak_observed_at ?? null,
14750
14755
  scheduled_break_active: item.scheduled_break_active ?? false,
14751
14756
  incoming_wip_current: item.incoming_wip_current ?? null,
14752
14757
  incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
14753
14758
  incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null,
14759
+ video_grid_badges: Array.isArray(item.video_grid_badges) ? item.video_grid_badges : [],
14754
14760
  show_exclamation: item.show_exclamation ?? void 0
14755
14761
  };
14756
14762
  workspaceMetricsStore.setOverview(metric);
@@ -21269,6 +21275,7 @@ var ICON_CONFIG = {
21269
21275
  "user-x": lucideReact.UserX,
21270
21276
  wrench: lucideReact.Wrench,
21271
21277
  activity: lucideReact.Activity,
21278
+ "clipboard-x": lucideReact.ClipboardX,
21272
21279
  "help-circle": lucideReact.HelpCircle
21273
21280
  };
21274
21281
  var humanizeIdleReasonLabel = (value) => {
@@ -38017,17 +38024,22 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
38017
38024
  const activeStreaks = visibleWorkspaces.map((workspace) => {
38018
38025
  const startedAt = workspace.video_grid_green_streak_started_at;
38019
38026
  const anchorAt = workspace.video_grid_green_streak_anchor_at;
38027
+ const observedAt = workspace.video_grid_green_streak_observed_at;
38020
38028
  const streakMinutes = workspace.video_grid_green_streak_minutes;
38021
38029
  const startedAtMs = parseTimestampMs(startedAt);
38022
38030
  const anchorAtMs = parseTimestampMs(anchorAt);
38031
+ const observedAtMs = parseTimestampMs(observedAt);
38023
38032
  const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs2);
38024
- const tickOriginAtMs = anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
38033
+ const tickOriginAtMs = Number.isFinite(observedAtMs) ? observedAtMs : anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
38034
+ const confirmedSeconds2 = Math.max(0, Math.floor((streakMinutes || 0) * 60));
38035
+ const visibleSeconds = confirmedSeconds2 + Math.max(0, Math.floor((nowMs2 - tickOriginAtMs) / 1e3));
38025
38036
  if (workspace.video_grid_green_streak_active === true && isFiniteNumber3(streakMinutes) && streakMinutes > 0 && startedAt && anchorAt && Number.isFinite(startedAtMs) && isFresh) {
38026
38037
  return {
38027
38038
  startedAt,
38028
38039
  startedAtMs,
38029
38040
  anchorAt,
38030
38041
  streakMinutes,
38042
+ visibleSeconds,
38031
38043
  tickOriginAtMs
38032
38044
  };
38033
38045
  }
@@ -38036,7 +38048,7 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
38036
38048
  if (activeStreaks.some((streak) => streak === null)) {
38037
38049
  return null;
38038
38050
  }
38039
- const strictestStartedAt = activeStreaks.reduce((latest, current) => current.streakMinutes < latest.streakMinutes || current.streakMinutes === latest.streakMinutes && current.startedAtMs > latest.startedAtMs ? current : latest);
38051
+ const strictestStartedAt = activeStreaks.reduce((latest, current) => current.visibleSeconds < latest.visibleSeconds || current.visibleSeconds === latest.visibleSeconds && current.startedAtMs > latest.startedAtMs ? current : latest);
38040
38052
  const confirmedSeconds = Math.max(0, Math.floor(strictestStartedAt.streakMinutes * 60));
38041
38053
  return {
38042
38054
  label: "All green",
@@ -38055,6 +38067,52 @@ var getVideoGridLegendLabel = (workspaces) => {
38055
38067
  }
38056
38068
  return visibleWorkspaces.some(isVideoGridRecentFlowEnabled) ? VIDEO_GRID_LEGEND_LABEL : MAP_GRID_LEGEND_LABEL;
38057
38069
  };
38070
+ var getStatusBadgeSignature = (workspace) => {
38071
+ const badges = workspace.video_grid_badges || [];
38072
+ return badges.map((badge) => [
38073
+ badge.kind,
38074
+ badge.label,
38075
+ badge.display_name,
38076
+ badge.palette_token,
38077
+ badge.icon_token,
38078
+ badge.anchor_minute,
38079
+ badge.reason_minute,
38080
+ badge.shift_elapsed_fraction,
38081
+ badge.title
38082
+ ].join(":")).join("|");
38083
+ };
38084
+ var VALID_STATUS_BADGE_ICON_TOKENS = /* @__PURE__ */ new Set([
38085
+ "alert-triangle",
38086
+ "refresh-cw",
38087
+ "package",
38088
+ "clock",
38089
+ "user-x",
38090
+ "wrench",
38091
+ "activity",
38092
+ "clipboard-x"
38093
+ ]);
38094
+ var normalizeStatusBadgeToken = (value) => String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
38095
+ var resolveVideoGridStatusBadgeIconToken = (badge) => {
38096
+ if (badge.kind === "no_plan") {
38097
+ return "clipboard-x";
38098
+ }
38099
+ const reasonTokens = /* @__PURE__ */ new Set([
38100
+ normalizeStatusBadgeToken(badge.label),
38101
+ normalizeStatusBadgeToken(badge.display_name),
38102
+ normalizeStatusBadgeToken(badge.title)
38103
+ ]);
38104
+ if (reasonTokens.has("no_material")) {
38105
+ return "package";
38106
+ }
38107
+ if (reasonTokens.has("machine_maintenance") || reasonTokens.has("machine_downtime")) {
38108
+ return "wrench";
38109
+ }
38110
+ const iconToken = normalizeStatusBadgeToken(badge.icon_token);
38111
+ if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {
38112
+ return iconToken;
38113
+ }
38114
+ return "activity";
38115
+ };
38058
38116
  function getTrendArrowAndColor(trend) {
38059
38117
  if (trend > 0) {
38060
38118
  return { arrow: "\u2191", color: "text-green-400" };
@@ -38086,6 +38144,7 @@ var VideoCard = React144__namespace.default.memo(({
38086
38144
  }) => {
38087
38145
  const videoRef = React144.useRef(null);
38088
38146
  const canvasRef = React144.useRef(null);
38147
+ const statusBadgeIdPrefix = React144.useId();
38089
38148
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
38090
38149
  const { isStale: isStreamStale } = useHlsStreamWithCropping(videoRef, canvasRef, {
38091
38150
  src: hlsUrl,
@@ -38112,6 +38171,7 @@ var VideoCard = React144__namespace.default.memo(({
38112
38171
  const shouldRenderMetricBadge = hasDisplayMetric;
38113
38172
  const badgeTitle = isHighEfficiencyOverride ? `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%` : hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
38114
38173
  const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;
38174
+ const statusBadges = workspace.video_grid_badges || [];
38115
38175
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "blue" ? "bg-[#0EA5E9]/30" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
38116
38176
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "blue" ? "bg-[#0EA5E9]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
38117
38177
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "blue" ? "Best" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -38190,6 +38250,61 @@ var VideoCard = React144__namespace.default.memo(({
38190
38250
  children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${compact ? "text-[10px]" : "text-xs"} font-semibold`, children: badgeLabel })
38191
38251
  }
38192
38252
  ) }),
38253
+ statusBadges.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
38254
+ "div",
38255
+ {
38256
+ "data-testid": "video-card-status-badges",
38257
+ className: `absolute ${compact ? "top-1.5 left-1.5 gap-1" : "top-2 left-2 gap-1.5"} z-30 flex items-center`,
38258
+ children: statusBadges.map((badge, index) => {
38259
+ const presentation = getIdleReasonPresentation({
38260
+ label: badge.label,
38261
+ displayName: badge.display_name,
38262
+ paletteToken: badge.palette_token,
38263
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
38264
+ isKnown: badge.is_known
38265
+ });
38266
+ const Icon2 = presentation.Icon;
38267
+ const tooltipText = presentation.displayName;
38268
+ const tooltipId = `video-card-status-tooltip-${statusBadgeIdPrefix}-${badge.kind}-${index}`;
38269
+ return /* @__PURE__ */ jsxRuntime.jsxs(
38270
+ "div",
38271
+ {
38272
+ "data-testid": `video-card-status-badge-${badge.kind}`,
38273
+ "aria-label": tooltipText,
38274
+ "aria-describedby": tooltipId,
38275
+ className: `group relative inline-flex shrink-0 items-center justify-center rounded-full bg-slate-950/70 border-2 shadow-[0_3px_10px_rgba(0,0,0,0.34),inset_0_0_0_1px_rgba(255,255,255,0.18)] ${compact ? "h-10 w-10" : "h-11 w-11"}`,
38276
+ style: {
38277
+ borderColor: presentation.hex
38278
+ },
38279
+ children: [
38280
+ /* @__PURE__ */ jsxRuntime.jsx(
38281
+ Icon2,
38282
+ {
38283
+ "aria-hidden": "true",
38284
+ className: `${compact ? "h-5 w-5" : "h-6 w-6"} text-white`,
38285
+ strokeWidth: 2.4
38286
+ }
38287
+ ),
38288
+ /* @__PURE__ */ jsxRuntime.jsxs(
38289
+ "span",
38290
+ {
38291
+ id: tooltipId,
38292
+ role: "tooltip",
38293
+ "data-testid": `video-card-status-tooltip-${badge.kind}`,
38294
+ className: "pointer-events-none absolute left-0 top-full mt-2 whitespace-nowrap rounded-md border border-white/10 bg-slate-950/95 px-2 py-1 text-[11px] font-semibold leading-none text-white opacity-0 shadow-[0_6px_18px_rgba(0,0,0,0.34)] transition-opacity duration-150 group-hover:opacity-100",
38295
+ children: [
38296
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 left-3 h-2 w-2 rotate-45 border-l border-t border-white/10 bg-slate-950/95" }),
38297
+ tooltipText
38298
+ ]
38299
+ }
38300
+ )
38301
+ ]
38302
+ },
38303
+ `${badge.kind}-${badge.label || index}-${badge.reason_minute ?? index}`
38304
+ );
38305
+ })
38306
+ }
38307
+ ),
38193
38308
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: `absolute bottom-0 left-0 right-0 ${compact ? "h-0.5" : "h-1"} bg-black/50 z-30`, children: /* @__PURE__ */ jsxRuntime.jsx(
38194
38309
  "div",
38195
38310
  {
@@ -38226,7 +38341,7 @@ var VideoCard = React144__namespace.default.memo(({
38226
38341
  }
38227
38342
  );
38228
38343
  }, (prevProps, nextProps) => {
38229
- if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.recent_flow_forced_zero_after_shift !== nextProps.workspace.recent_flow_forced_zero_after_shift || prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
38344
+ if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.recent_flow_forced_zero_after_shift !== nextProps.workspace.recent_flow_forced_zero_after_shift || prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || getStatusBadgeSignature(prevProps.workspace) !== getStatusBadgeSignature(nextProps.workspace) || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
38230
38345
  return false;
38231
38346
  }
38232
38347
  if (prevProps.workspace.workspace_uuid !== nextProps.workspace.workspace_uuid || prevProps.workspace.workspace_name !== nextProps.workspace.workspace_name || prevProps.workspace.line_id !== nextProps.workspace.line_id) {
@@ -64852,6 +64967,8 @@ var EMPTY_LINE_IDS = [];
64852
64967
  var EMPTY_WORKSPACES = [];
64853
64968
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
64854
64969
  var ALL_GREEN_MILESTONE_DURATION_MS = 6e3;
64970
+ var ALL_GREEN_CELEBRATION_SEEN_PREFIX = "optifye:all-green-celebration:v1:";
64971
+ var ALL_GREEN_MILESTONE_SEEN_PREFIX = "optifye:all-green-milestone:v1:";
64855
64972
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
64856
64973
  const safeElapsedSeconds = Math.max(1, Math.floor(elapsedSeconds));
64857
64974
  const minutes = Math.floor(safeElapsedSeconds / 60);
@@ -64862,6 +64979,29 @@ var getAllGreenBackendVisibleSeconds = (confirmedSeconds, tickOriginAtMs, nowMs2
64862
64979
  const localSecondOffset = Math.max(0, Math.floor((nowMs2 - tickOriginAtMs) / 1e3));
64863
64980
  return confirmedSeconds + localSecondOffset;
64864
64981
  };
64982
+ var getSessionSeenValue = (key) => {
64983
+ if (typeof window === "undefined") {
64984
+ return null;
64985
+ }
64986
+ try {
64987
+ const storage = window.sessionStorage;
64988
+ return storage ? storage.getItem(key) : null;
64989
+ } catch {
64990
+ return null;
64991
+ }
64992
+ };
64993
+ var setSessionSeenValue = (key) => {
64994
+ if (typeof window === "undefined") {
64995
+ return;
64996
+ }
64997
+ try {
64998
+ const storage = window.sessionStorage;
64999
+ storage?.setItem(key, "1");
65000
+ } catch {
65001
+ }
65002
+ };
65003
+ var buildAllGreenCelebrationSeenKey = (identity) => `${ALL_GREEN_CELEBRATION_SEEN_PREFIX}${identity}`;
65004
+ var buildAllGreenMilestoneSeenKey = (identity, milestoneSeconds) => `${ALL_GREEN_MILESTONE_SEEN_PREFIX}${identity}:${milestoneSeconds}`;
64865
65005
  var LoadingPageCmp = LoadingPage_default;
64866
65006
  var LoadingOverlayCmp = LoadingOverlay_default;
64867
65007
  function HomeView({
@@ -65422,6 +65562,12 @@ function HomeView({
65422
65562
  if (milestoneTracking.identity !== allGreenMilestoneIdentity) {
65423
65563
  milestoneTracking.identity = allGreenMilestoneIdentity;
65424
65564
  milestoneTracking.lastMilestoneSeconds = currentMilestoneSeconds;
65565
+ if (currentMilestoneSeconds !== null) {
65566
+ setSessionSeenValue(buildAllGreenMilestoneSeenKey(
65567
+ allGreenMilestoneIdentity,
65568
+ currentMilestoneSeconds
65569
+ ));
65570
+ }
65425
65571
  dismissGreenStreakMilestoneBanner();
65426
65572
  return;
65427
65573
  }
@@ -65431,6 +65577,15 @@ function HomeView({
65431
65577
  }
65432
65578
  if ((milestoneTracking.lastMilestoneSeconds ?? 0) < currentMilestone.milestoneSeconds) {
65433
65579
  milestoneTracking.lastMilestoneSeconds = currentMilestone.milestoneSeconds;
65580
+ const milestoneSeenKey = buildAllGreenMilestoneSeenKey(
65581
+ allGreenMilestoneIdentity,
65582
+ currentMilestone.milestoneSeconds
65583
+ );
65584
+ if (getSessionSeenValue(milestoneSeenKey) === "1") {
65585
+ dismissGreenStreakMilestoneBanner();
65586
+ return;
65587
+ }
65588
+ setSessionSeenValue(milestoneSeenKey);
65434
65589
  triggerGreenStreakMilestoneBanner(currentMilestone);
65435
65590
  }
65436
65591
  }, [
@@ -65449,6 +65604,9 @@ function HomeView({
65449
65604
  if (transitionState.signature !== allGreenCelebrationSignature) {
65450
65605
  transitionState.signature = allGreenCelebrationSignature;
65451
65606
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65607
+ if (currentValidStreakIdentity) {
65608
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65609
+ }
65452
65610
  dismissAllGreenCelebration();
65453
65611
  return;
65454
65612
  }
@@ -65459,11 +65617,18 @@ function HomeView({
65459
65617
  }
65460
65618
  if (transitionState.previousValidStreakIdentity === null) {
65461
65619
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65620
+ const celebrationSeenKey = buildAllGreenCelebrationSeenKey(currentValidStreakIdentity);
65621
+ if (getSessionSeenValue(celebrationSeenKey) === "1") {
65622
+ dismissAllGreenCelebration();
65623
+ return;
65624
+ }
65625
+ setSessionSeenValue(celebrationSeenKey);
65462
65626
  triggerAllGreenCelebration();
65463
65627
  return;
65464
65628
  }
65465
65629
  if (transitionState.previousValidStreakIdentity !== currentValidStreakIdentity) {
65466
65630
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65631
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65467
65632
  dismissAllGreenCelebration();
65468
65633
  }
65469
65634
  }, [
@@ -84024,11 +84189,19 @@ var normalizeSummary = (value) => ({
84024
84189
  avg_idle_per_workstation: normalizeIdleMetric(value?.avg_idle_per_workstation)
84025
84190
  });
84026
84191
  var normalizeLineRow = (value) => ({
84192
+ row_type: value?.row_type ?? null,
84027
84193
  line_id: value?.line_id,
84028
84194
  line_name: value?.line_name?.trim() || value?.line_id || "",
84029
84195
  avg_efficiency: normalizeNumber(value?.avg_efficiency),
84030
84196
  previous_avg_efficiency: normalizeNumber(value?.previous_avg_efficiency),
84031
- delta_pp: normalizeNumber(value?.delta_pp)
84197
+ delta_pp: normalizeNumber(value?.delta_pp),
84198
+ factory_id: value?.factory_id ?? null,
84199
+ factory_area_id: value?.factory_area_id ?? null,
84200
+ factory_area_key: value?.factory_area_key ?? null,
84201
+ factory_area_name: value?.factory_area_name?.trim() || null,
84202
+ factory_area_sort_order: normalizeNumber(value?.factory_area_sort_order),
84203
+ factory_area_enabled: typeof value?.factory_area_enabled === "boolean" ? value.factory_area_enabled : null,
84204
+ line_count: normalizeNumber(value?.line_count)
84032
84205
  });
84033
84206
  var normalizePoorestLines = (value) => ({
84034
84207
  output: (value?.output || []).map((item) => normalizeLineRow(item)),
@@ -84372,6 +84545,23 @@ var toNumber4 = (value) => {
84372
84545
  return null;
84373
84546
  };
84374
84547
  var roundOne = (value) => Math.round(value * 10) / 10;
84548
+ var isFiniteNumber4 = (value) => typeof value === "number" && Number.isFinite(value);
84549
+ var averageFinite = (values) => {
84550
+ const finiteValues = values.filter(isFiniteNumber4);
84551
+ if (finiteValues.length === 0) return null;
84552
+ return finiteValues.reduce((sum, value) => sum + value, 0) / finiteValues.length;
84553
+ };
84554
+ var getEnabledAreaInfo2 = (row) => {
84555
+ const areaId = row.factory_area_id?.trim();
84556
+ const areaName = row.factory_area_name?.trim();
84557
+ if (!areaId || !areaName) return null;
84558
+ if (row.factory_area_enabled !== true) return null;
84559
+ return {
84560
+ areaId,
84561
+ areaName,
84562
+ factoryId: row.factory_id?.trim() || null
84563
+ };
84564
+ };
84375
84565
  var formatIdleDuration = (seconds) => {
84376
84566
  if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
84377
84567
  return "--";
@@ -85106,11 +85296,49 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85106
85296
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
85107
85297
  const mergedPoorestLines = React144__namespace.default.useMemo(() => {
85108
85298
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
85109
- return rows.slice(0, 3).map((line) => {
85299
+ const lineRows = [];
85300
+ const areaGroups = /* @__PURE__ */ new Map();
85301
+ rows.forEach((line) => {
85302
+ if (line.row_type === "area") {
85303
+ const efficiency = toNumber4(line.avg_efficiency);
85304
+ if (efficiency === null) return;
85305
+ const previousEfficiency = toNumber4(line.previous_avg_efficiency);
85306
+ const lineCount = Math.max(1, Math.round(toNumber4(line.line_count) || 1));
85307
+ lineRows.push({
85308
+ rowType: "area",
85309
+ id: line.line_id || `area:${line.factory_area_id || line.line_name || "unknown"}`,
85310
+ name: line.factory_area_name?.trim() || line.line_name?.trim() || "Unknown Area",
85311
+ efficiency: roundOne(efficiency),
85312
+ previousEfficiency: previousEfficiency === null ? null : roundOne(previousEfficiency),
85313
+ delta: toNumber4(line.delta_pp) ?? (previousEfficiency === null ? null : roundOne(efficiency - previousEfficiency)),
85314
+ supervisor: `${lineCount} ${lineCount === 1 ? "line" : "lines"}`,
85315
+ supervisorImage: null,
85316
+ lineCount,
85317
+ factoryId: line.factory_id ?? null,
85318
+ factoryAreaId: line.factory_area_id ?? null
85319
+ });
85320
+ return;
85321
+ }
85322
+ const area = getEnabledAreaInfo2(line);
85323
+ if (area) {
85324
+ const group = areaGroups.get(area.areaId);
85325
+ if (group) {
85326
+ group.lines.push(line);
85327
+ } else {
85328
+ areaGroups.set(area.areaId, {
85329
+ areaId: area.areaId,
85330
+ areaName: area.areaName,
85331
+ factoryId: area.factoryId,
85332
+ lines: [line]
85333
+ });
85334
+ }
85335
+ return;
85336
+ }
85110
85337
  const lineId = line.line_id || "";
85111
85338
  const supervisors = supervisorsByLineId.get(lineId) || [];
85112
85339
  const supervisor = supervisors[0];
85113
- return {
85340
+ lineRows.push({
85341
+ rowType: "line",
85114
85342
  id: lineId,
85115
85343
  name: line.line_name?.trim() || "Unknown Line",
85116
85344
  efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
@@ -85118,8 +85346,31 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85118
85346
  delta: toNumber4(line.delta_pp),
85119
85347
  supervisor: supervisor?.displayName || "Unassigned",
85120
85348
  supervisorImage: supervisor?.profilePhotoUrl ?? null
85121
- };
85349
+ });
85350
+ });
85351
+ areaGroups.forEach((group) => {
85352
+ const efficiency = averageFinite(group.lines.map((line) => toNumber4(line.avg_efficiency)));
85353
+ if (efficiency === null) return;
85354
+ const previousEfficiency = averageFinite(group.lines.map((line) => toNumber4(line.previous_avg_efficiency)));
85355
+ const lineCount = group.lines.length;
85356
+ lineRows.push({
85357
+ rowType: "area",
85358
+ id: `area:${group.areaId}`,
85359
+ name: group.areaName,
85360
+ efficiency: roundOne(efficiency),
85361
+ previousEfficiency: previousEfficiency === null ? null : roundOne(previousEfficiency),
85362
+ delta: previousEfficiency === null ? null : roundOne(efficiency - previousEfficiency),
85363
+ supervisor: `${lineCount} ${lineCount === 1 ? "line" : "lines"}`,
85364
+ supervisorImage: null,
85365
+ lineCount,
85366
+ factoryId: group.factoryId,
85367
+ factoryAreaId: group.areaId
85368
+ });
85122
85369
  });
85370
+ return lineRows.sort((left, right) => {
85371
+ if (left.efficiency !== right.efficiency) return left.efficiency - right.efficiency;
85372
+ return left.name.localeCompare(right.name, void 0, { sensitivity: "base", numeric: true });
85373
+ }).slice(0, 3);
85123
85374
  }, [poorestLineMode, snapshot.data.poorest_lines, supervisorsByLineId]);
85124
85375
  const showPoorestModeToggle = !!availableLineModes?.has_output && !!availableLineModes?.has_uptime;
85125
85376
  const poorestMetricLabel = poorestLineMode === "uptime" ? "Uptime" : "Efficiency";
@@ -85165,7 +85416,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85165
85416
  ] }),
85166
85417
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex flex-col p-0 overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y divide-slate-50 flex-1 px-5", children: [
85167
85418
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2", children: [
85168
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider min-w-[120px]", children: "Line" }),
85419
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider min-w-[120px]", children: "Line / Area" }),
85169
85420
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider text-left shrink-0 w-[110px] flex items-center justify-between", children: poorestMetricLabel })
85170
85421
  ] }),
85171
85422
  showSnapshotSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewListSkeleton, {}) : mergedPoorestLines.length > 0 ? mergedPoorestLines.map((line) => {
@@ -85173,7 +85424,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85173
85424
  return /* @__PURE__ */ jsxRuntime.jsx(
85174
85425
  "div",
85175
85426
  {
85176
- onClick: () => onLineClick(line.id, line.name),
85427
+ onClick: () => onLineClick(line),
85177
85428
  className: "block py-3 hover:bg-slate-50/50 transition-colors cursor-pointer group relative",
85178
85429
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4", children: [
85179
85430
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
@@ -85184,7 +85435,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85184
85435
  alt: line.supervisor,
85185
85436
  className: "w-full h-full object-cover"
85186
85437
  }
85187
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-[10px] font-bold text-slate-500", children: line.supervisor.split(" ").map((part) => part[0]).join("").slice(0, 2) }) }),
85438
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-[10px] font-bold text-slate-500", children: (line.rowType === "area" ? line.name : line.supervisor).split(" ").map((part) => part[0]).join("").slice(0, 2) }) }),
85188
85439
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
85189
85440
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-bold text-slate-800 text-[13px] truncate group-hover:text-indigo-600 transition-colors", children: line.name }) }),
85190
85441
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 mt-0.5 flex-wrap", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-slate-400 font-medium", children: line.supervisor }) })
@@ -86474,18 +86725,33 @@ var PlantHeadView = () => {
86474
86725
  resolvedOperationalToday
86475
86726
  ]
86476
86727
  );
86477
- const handleOpenLineDetails = React144__namespace.default.useCallback((lineId, lineName) => {
86728
+ const handleOpenLineDetails = React144__namespace.default.useCallback((line) => {
86478
86729
  trackCoreEvent("Operations Overview Line Clicked", {
86479
- line_id: lineId,
86480
- line_name: lineName,
86730
+ line_id: line.rowType === "line" ? line.id : null,
86731
+ line_name: line.name,
86732
+ row_type: line.rowType,
86733
+ factory_id: line.factoryId ?? null,
86734
+ factory_area_id: line.factoryAreaId ?? null,
86481
86735
  range_start: dateRange.startKey,
86482
86736
  range_end: dateRange.endKey
86483
86737
  });
86738
+ if (line.rowType === "area" && line.factoryId && line.factoryAreaId) {
86739
+ const params = new URLSearchParams({
86740
+ factory_id: line.factoryId,
86741
+ factory_area_id: line.factoryAreaId
86742
+ });
86743
+ navigate(`/kpis?${params.toString()}`);
86744
+ return;
86745
+ }
86746
+ if (line.rowType === "area") {
86747
+ navigate("/kpis");
86748
+ return;
86749
+ }
86484
86750
  if (isLiveScope) {
86485
- navigate(`/kpis/${lineId}?returnTo=${encodeURIComponent("/")}`);
86751
+ navigate(`/kpis/${line.id}?returnTo=${encodeURIComponent("/")}`);
86486
86752
  return;
86487
86753
  }
86488
- navigate(buildLineMonthlyHistoryUrl(lineId));
86754
+ navigate(buildLineMonthlyHistoryUrl(line.id));
86489
86755
  }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, isLiveScope, navigate]);
86490
86756
  useOperationsOverviewRefresh({
86491
86757
  store,