@optifye/dashboard-core 6.12.30 → 6.12.32

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.
@@ -212,6 +212,7 @@ interface WorkspaceMetrics {
212
212
  video_grid_green_streak_minutes?: number | null;
213
213
  video_grid_green_streak_anchor_at?: string | null;
214
214
  video_grid_green_streak_started_at?: string | null;
215
+ video_grid_green_streak_observed_at?: string | null;
215
216
  scheduled_break_active?: boolean;
216
217
  incoming_wip_current?: number | null;
217
218
  incoming_wip_effective_at?: string | null;
@@ -212,6 +212,7 @@ interface WorkspaceMetrics {
212
212
  video_grid_green_streak_minutes?: number | null;
213
213
  video_grid_green_streak_anchor_at?: string | null;
214
214
  video_grid_green_streak_started_at?: string | null;
215
+ video_grid_green_streak_observed_at?: string | null;
215
216
  scheduled_break_active?: boolean;
216
217
  incoming_wip_current?: number | null;
217
218
  incoming_wip_effective_at?: string | null;
@@ -1,2 +1,2 @@
1
- export { B as RecentFlowSnapshotGrid, R as RecentFlowSnapshotGridProps, W as WorkspaceMetrics, e as WorkspaceVideoStream } from './automation-Jf6Isg6G.mjs';
1
+ export { B as RecentFlowSnapshotGrid, R as RecentFlowSnapshotGridProps, W as WorkspaceMetrics, e as WorkspaceVideoStream } from './automation-ZIumB5W9.mjs';
2
2
  import 'react';
@@ -1,2 +1,2 @@
1
- export { B as RecentFlowSnapshotGrid, R as RecentFlowSnapshotGridProps, W as WorkspaceMetrics, e as WorkspaceVideoStream } from './automation-Jf6Isg6G.js';
1
+ export { B as RecentFlowSnapshotGrid, R as RecentFlowSnapshotGridProps, W as WorkspaceMetrics, e as WorkspaceVideoStream } from './automation-ZIumB5W9.js';
2
2
  import 'react';
@@ -1982,6 +1982,54 @@ var getStatusBadgeSignature = (workspace) => {
1982
1982
  badge.title
1983
1983
  ].join(":")).join("|");
1984
1984
  };
1985
+ var VALID_STATUS_BADGE_ICON_TOKENS = /* @__PURE__ */ new Set([
1986
+ "alert-triangle",
1987
+ "refresh-cw",
1988
+ "package",
1989
+ "clock",
1990
+ "user-x",
1991
+ "wrench",
1992
+ "activity",
1993
+ "clipboard-x"
1994
+ ]);
1995
+ var normalizeStatusBadgeToken = (value) => String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
1996
+ var TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS = /* @__PURE__ */ new Set([
1997
+ "machine_maintenance",
1998
+ "machine_downtime",
1999
+ "no_material"
2000
+ ]);
2001
+ var shouldRenderVideoGridStatusBadge = (badge) => {
2002
+ if (badge.kind !== "idle_reason") {
2003
+ return true;
2004
+ }
2005
+ const reasonTokens = [
2006
+ normalizeStatusBadgeToken(badge.label),
2007
+ normalizeStatusBadgeToken(badge.display_name),
2008
+ normalizeStatusBadgeToken(badge.title)
2009
+ ];
2010
+ return !reasonTokens.some((token) => TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS.has(token));
2011
+ };
2012
+ var resolveVideoGridStatusBadgeIconToken = (badge) => {
2013
+ if (badge.kind === "no_plan") {
2014
+ return "clipboard-x";
2015
+ }
2016
+ const reasonTokens = /* @__PURE__ */ new Set([
2017
+ normalizeStatusBadgeToken(badge.label),
2018
+ normalizeStatusBadgeToken(badge.display_name),
2019
+ normalizeStatusBadgeToken(badge.title)
2020
+ ]);
2021
+ if (reasonTokens.has("no_material")) {
2022
+ return "package";
2023
+ }
2024
+ if (reasonTokens.has("machine_maintenance") || reasonTokens.has("machine_downtime")) {
2025
+ return "wrench";
2026
+ }
2027
+ const iconToken = normalizeStatusBadgeToken(badge.icon_token);
2028
+ if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {
2029
+ return iconToken;
2030
+ }
2031
+ return "activity";
2032
+ };
1985
2033
  function getTrendArrowAndColor(trend) {
1986
2034
  if (trend > 0) {
1987
2035
  return { arrow: "\u2191", color: "text-green-400" };
@@ -2040,7 +2088,7 @@ var VideoCard = React__default.default.memo(({
2040
2088
  const shouldRenderMetricBadge = hasDisplayMetric;
2041
2089
  const badgeTitle = isHighEfficiencyOverride ? `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%` : hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
2042
2090
  const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;
2043
- const statusBadges = workspace.video_grid_badges || [];
2091
+ const statusBadges = (workspace.video_grid_badges || []).filter(shouldRenderVideoGridStatusBadge);
2044
2092
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "blue" ? "bg-[#0EA5E9]/30" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
2045
2093
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "blue" ? "bg-[#0EA5E9]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
2046
2094
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "blue" ? "Best" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -2129,7 +2177,7 @@ var VideoCard = React__default.default.memo(({
2129
2177
  label: badge.label,
2130
2178
  displayName: badge.display_name,
2131
2179
  paletteToken: badge.palette_token,
2132
- iconToken: badge.icon_token,
2180
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
2133
2181
  isKnown: badge.is_known
2134
2182
  });
2135
2183
  const Icon = presentation.Icon;
@@ -1975,6 +1975,54 @@ var getStatusBadgeSignature = (workspace) => {
1975
1975
  badge.title
1976
1976
  ].join(":")).join("|");
1977
1977
  };
1978
+ var VALID_STATUS_BADGE_ICON_TOKENS = /* @__PURE__ */ new Set([
1979
+ "alert-triangle",
1980
+ "refresh-cw",
1981
+ "package",
1982
+ "clock",
1983
+ "user-x",
1984
+ "wrench",
1985
+ "activity",
1986
+ "clipboard-x"
1987
+ ]);
1988
+ var normalizeStatusBadgeToken = (value) => String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
1989
+ var TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS = /* @__PURE__ */ new Set([
1990
+ "machine_maintenance",
1991
+ "machine_downtime",
1992
+ "no_material"
1993
+ ]);
1994
+ var shouldRenderVideoGridStatusBadge = (badge) => {
1995
+ if (badge.kind !== "idle_reason") {
1996
+ return true;
1997
+ }
1998
+ const reasonTokens = [
1999
+ normalizeStatusBadgeToken(badge.label),
2000
+ normalizeStatusBadgeToken(badge.display_name),
2001
+ normalizeStatusBadgeToken(badge.title)
2002
+ ];
2003
+ return !reasonTokens.some((token) => TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS.has(token));
2004
+ };
2005
+ var resolveVideoGridStatusBadgeIconToken = (badge) => {
2006
+ if (badge.kind === "no_plan") {
2007
+ return "clipboard-x";
2008
+ }
2009
+ const reasonTokens = /* @__PURE__ */ new Set([
2010
+ normalizeStatusBadgeToken(badge.label),
2011
+ normalizeStatusBadgeToken(badge.display_name),
2012
+ normalizeStatusBadgeToken(badge.title)
2013
+ ]);
2014
+ if (reasonTokens.has("no_material")) {
2015
+ return "package";
2016
+ }
2017
+ if (reasonTokens.has("machine_maintenance") || reasonTokens.has("machine_downtime")) {
2018
+ return "wrench";
2019
+ }
2020
+ const iconToken = normalizeStatusBadgeToken(badge.icon_token);
2021
+ if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {
2022
+ return iconToken;
2023
+ }
2024
+ return "activity";
2025
+ };
1978
2026
  function getTrendArrowAndColor(trend) {
1979
2027
  if (trend > 0) {
1980
2028
  return { arrow: "\u2191", color: "text-green-400" };
@@ -2033,7 +2081,7 @@ var VideoCard = React.memo(({
2033
2081
  const shouldRenderMetricBadge = hasDisplayMetric;
2034
2082
  const badgeTitle = isHighEfficiencyOverride ? `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%` : hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
2035
2083
  const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;
2036
- const statusBadges = workspace.video_grid_badges || [];
2084
+ const statusBadges = (workspace.video_grid_badges || []).filter(shouldRenderVideoGridStatusBadge);
2037
2085
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "blue" ? "bg-[#0EA5E9]/30" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
2038
2086
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "blue" ? "bg-[#0EA5E9]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
2039
2087
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "blue" ? "Best" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -2122,7 +2170,7 @@ var VideoCard = React.memo(({
2122
2170
  label: badge.label,
2123
2171
  displayName: badge.display_name,
2124
2172
  paletteToken: badge.palette_token,
2125
- iconToken: badge.icon_token,
2173
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
2126
2174
  isKnown: badge.is_known
2127
2175
  });
2128
2176
  const Icon = presentation.Icon;
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-Jf6Isg6G.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-Jf6Isg6G.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';
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-Jf6Isg6G.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-Jf6Isg6G.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';
package/dist/index.js CHANGED
@@ -14751,6 +14751,7 @@ var transformMonitorWorkspaceMetrics = ({
14751
14751
  video_grid_green_streak_minutes: item.video_grid_green_streak_minutes ?? null,
14752
14752
  video_grid_green_streak_anchor_at: item.video_grid_green_streak_anchor_at ?? null,
14753
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,
14754
14755
  scheduled_break_active: item.scheduled_break_active ?? false,
14755
14756
  incoming_wip_current: item.incoming_wip_current ?? null,
14756
14757
  incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
@@ -38023,17 +38024,22 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
38023
38024
  const activeStreaks = visibleWorkspaces.map((workspace) => {
38024
38025
  const startedAt = workspace.video_grid_green_streak_started_at;
38025
38026
  const anchorAt = workspace.video_grid_green_streak_anchor_at;
38027
+ const observedAt = workspace.video_grid_green_streak_observed_at;
38026
38028
  const streakMinutes = workspace.video_grid_green_streak_minutes;
38027
38029
  const startedAtMs = parseTimestampMs(startedAt);
38028
38030
  const anchorAtMs = parseTimestampMs(anchorAt);
38031
+ const observedAtMs = parseTimestampMs(observedAt);
38029
38032
  const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs2);
38030
- 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));
38031
38036
  if (workspace.video_grid_green_streak_active === true && isFiniteNumber3(streakMinutes) && streakMinutes > 0 && startedAt && anchorAt && Number.isFinite(startedAtMs) && isFresh) {
38032
38037
  return {
38033
38038
  startedAt,
38034
38039
  startedAtMs,
38035
38040
  anchorAt,
38036
38041
  streakMinutes,
38042
+ visibleSeconds,
38037
38043
  tickOriginAtMs
38038
38044
  };
38039
38045
  }
@@ -38042,7 +38048,7 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
38042
38048
  if (activeStreaks.some((streak) => streak === null)) {
38043
38049
  return null;
38044
38050
  }
38045
- 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);
38046
38052
  const confirmedSeconds = Math.max(0, Math.floor(strictestStartedAt.streakMinutes * 60));
38047
38053
  return {
38048
38054
  label: "All green",
@@ -38075,6 +38081,54 @@ var getStatusBadgeSignature = (workspace) => {
38075
38081
  badge.title
38076
38082
  ].join(":")).join("|");
38077
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 TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS = /* @__PURE__ */ new Set([
38096
+ "machine_maintenance",
38097
+ "machine_downtime",
38098
+ "no_material"
38099
+ ]);
38100
+ var shouldRenderVideoGridStatusBadge = (badge) => {
38101
+ if (badge.kind !== "idle_reason") {
38102
+ return true;
38103
+ }
38104
+ const reasonTokens = [
38105
+ normalizeStatusBadgeToken(badge.label),
38106
+ normalizeStatusBadgeToken(badge.display_name),
38107
+ normalizeStatusBadgeToken(badge.title)
38108
+ ];
38109
+ return !reasonTokens.some((token) => TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS.has(token));
38110
+ };
38111
+ var resolveVideoGridStatusBadgeIconToken = (badge) => {
38112
+ if (badge.kind === "no_plan") {
38113
+ return "clipboard-x";
38114
+ }
38115
+ const reasonTokens = /* @__PURE__ */ new Set([
38116
+ normalizeStatusBadgeToken(badge.label),
38117
+ normalizeStatusBadgeToken(badge.display_name),
38118
+ normalizeStatusBadgeToken(badge.title)
38119
+ ]);
38120
+ if (reasonTokens.has("no_material")) {
38121
+ return "package";
38122
+ }
38123
+ if (reasonTokens.has("machine_maintenance") || reasonTokens.has("machine_downtime")) {
38124
+ return "wrench";
38125
+ }
38126
+ const iconToken = normalizeStatusBadgeToken(badge.icon_token);
38127
+ if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {
38128
+ return iconToken;
38129
+ }
38130
+ return "activity";
38131
+ };
38078
38132
  function getTrendArrowAndColor(trend) {
38079
38133
  if (trend > 0) {
38080
38134
  return { arrow: "\u2191", color: "text-green-400" };
@@ -38133,7 +38187,7 @@ var VideoCard = React144__namespace.default.memo(({
38133
38187
  const shouldRenderMetricBadge = hasDisplayMetric;
38134
38188
  const badgeTitle = isHighEfficiencyOverride ? `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%` : hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
38135
38189
  const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;
38136
- const statusBadges = workspace.video_grid_badges || [];
38190
+ const statusBadges = (workspace.video_grid_badges || []).filter(shouldRenderVideoGridStatusBadge);
38137
38191
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "blue" ? "bg-[#0EA5E9]/30" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
38138
38192
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "blue" ? "bg-[#0EA5E9]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
38139
38193
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "blue" ? "Best" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -38222,7 +38276,7 @@ var VideoCard = React144__namespace.default.memo(({
38222
38276
  label: badge.label,
38223
38277
  displayName: badge.display_name,
38224
38278
  paletteToken: badge.palette_token,
38225
- iconToken: badge.icon_token,
38279
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
38226
38280
  isKnown: badge.is_known
38227
38281
  });
38228
38282
  const Icon2 = presentation.Icon;
@@ -64929,6 +64983,8 @@ var EMPTY_LINE_IDS = [];
64929
64983
  var EMPTY_WORKSPACES = [];
64930
64984
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
64931
64985
  var ALL_GREEN_MILESTONE_DURATION_MS = 6e3;
64986
+ var ALL_GREEN_CELEBRATION_SEEN_PREFIX = "optifye:all-green-celebration:v1:";
64987
+ var ALL_GREEN_MILESTONE_SEEN_PREFIX = "optifye:all-green-milestone:v1:";
64932
64988
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
64933
64989
  const safeElapsedSeconds = Math.max(1, Math.floor(elapsedSeconds));
64934
64990
  const minutes = Math.floor(safeElapsedSeconds / 60);
@@ -64939,6 +64995,29 @@ var getAllGreenBackendVisibleSeconds = (confirmedSeconds, tickOriginAtMs, nowMs2
64939
64995
  const localSecondOffset = Math.max(0, Math.floor((nowMs2 - tickOriginAtMs) / 1e3));
64940
64996
  return confirmedSeconds + localSecondOffset;
64941
64997
  };
64998
+ var getSessionSeenValue = (key) => {
64999
+ if (typeof window === "undefined") {
65000
+ return null;
65001
+ }
65002
+ try {
65003
+ const storage = window.sessionStorage;
65004
+ return storage ? storage.getItem(key) : null;
65005
+ } catch {
65006
+ return null;
65007
+ }
65008
+ };
65009
+ var setSessionSeenValue = (key) => {
65010
+ if (typeof window === "undefined") {
65011
+ return;
65012
+ }
65013
+ try {
65014
+ const storage = window.sessionStorage;
65015
+ storage?.setItem(key, "1");
65016
+ } catch {
65017
+ }
65018
+ };
65019
+ var buildAllGreenCelebrationSeenKey = (identity) => `${ALL_GREEN_CELEBRATION_SEEN_PREFIX}${identity}`;
65020
+ var buildAllGreenMilestoneSeenKey = (identity, milestoneSeconds) => `${ALL_GREEN_MILESTONE_SEEN_PREFIX}${identity}:${milestoneSeconds}`;
64942
65021
  var LoadingPageCmp = LoadingPage_default;
64943
65022
  var LoadingOverlayCmp = LoadingOverlay_default;
64944
65023
  function HomeView({
@@ -65499,6 +65578,12 @@ function HomeView({
65499
65578
  if (milestoneTracking.identity !== allGreenMilestoneIdentity) {
65500
65579
  milestoneTracking.identity = allGreenMilestoneIdentity;
65501
65580
  milestoneTracking.lastMilestoneSeconds = currentMilestoneSeconds;
65581
+ if (currentMilestoneSeconds !== null) {
65582
+ setSessionSeenValue(buildAllGreenMilestoneSeenKey(
65583
+ allGreenMilestoneIdentity,
65584
+ currentMilestoneSeconds
65585
+ ));
65586
+ }
65502
65587
  dismissGreenStreakMilestoneBanner();
65503
65588
  return;
65504
65589
  }
@@ -65508,6 +65593,15 @@ function HomeView({
65508
65593
  }
65509
65594
  if ((milestoneTracking.lastMilestoneSeconds ?? 0) < currentMilestone.milestoneSeconds) {
65510
65595
  milestoneTracking.lastMilestoneSeconds = currentMilestone.milestoneSeconds;
65596
+ const milestoneSeenKey = buildAllGreenMilestoneSeenKey(
65597
+ allGreenMilestoneIdentity,
65598
+ currentMilestone.milestoneSeconds
65599
+ );
65600
+ if (getSessionSeenValue(milestoneSeenKey) === "1") {
65601
+ dismissGreenStreakMilestoneBanner();
65602
+ return;
65603
+ }
65604
+ setSessionSeenValue(milestoneSeenKey);
65511
65605
  triggerGreenStreakMilestoneBanner(currentMilestone);
65512
65606
  }
65513
65607
  }, [
@@ -65526,6 +65620,9 @@ function HomeView({
65526
65620
  if (transitionState.signature !== allGreenCelebrationSignature) {
65527
65621
  transitionState.signature = allGreenCelebrationSignature;
65528
65622
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65623
+ if (currentValidStreakIdentity) {
65624
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65625
+ }
65529
65626
  dismissAllGreenCelebration();
65530
65627
  return;
65531
65628
  }
@@ -65536,11 +65633,18 @@ function HomeView({
65536
65633
  }
65537
65634
  if (transitionState.previousValidStreakIdentity === null) {
65538
65635
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65636
+ const celebrationSeenKey = buildAllGreenCelebrationSeenKey(currentValidStreakIdentity);
65637
+ if (getSessionSeenValue(celebrationSeenKey) === "1") {
65638
+ dismissAllGreenCelebration();
65639
+ return;
65640
+ }
65641
+ setSessionSeenValue(celebrationSeenKey);
65539
65642
  triggerAllGreenCelebration();
65540
65643
  return;
65541
65644
  }
65542
65645
  if (transitionState.previousValidStreakIdentity !== currentValidStreakIdentity) {
65543
65646
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65647
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65544
65648
  dismissAllGreenCelebration();
65545
65649
  }
65546
65650
  }, [
@@ -84101,11 +84205,19 @@ var normalizeSummary = (value) => ({
84101
84205
  avg_idle_per_workstation: normalizeIdleMetric(value?.avg_idle_per_workstation)
84102
84206
  });
84103
84207
  var normalizeLineRow = (value) => ({
84208
+ row_type: value?.row_type ?? null,
84104
84209
  line_id: value?.line_id,
84105
84210
  line_name: value?.line_name?.trim() || value?.line_id || "",
84106
84211
  avg_efficiency: normalizeNumber(value?.avg_efficiency),
84107
84212
  previous_avg_efficiency: normalizeNumber(value?.previous_avg_efficiency),
84108
- delta_pp: normalizeNumber(value?.delta_pp)
84213
+ delta_pp: normalizeNumber(value?.delta_pp),
84214
+ factory_id: value?.factory_id ?? null,
84215
+ factory_area_id: value?.factory_area_id ?? null,
84216
+ factory_area_key: value?.factory_area_key ?? null,
84217
+ factory_area_name: value?.factory_area_name?.trim() || null,
84218
+ factory_area_sort_order: normalizeNumber(value?.factory_area_sort_order),
84219
+ factory_area_enabled: typeof value?.factory_area_enabled === "boolean" ? value.factory_area_enabled : null,
84220
+ line_count: normalizeNumber(value?.line_count)
84109
84221
  });
84110
84222
  var normalizePoorestLines = (value) => ({
84111
84223
  output: (value?.output || []).map((item) => normalizeLineRow(item)),
@@ -84449,6 +84561,23 @@ var toNumber4 = (value) => {
84449
84561
  return null;
84450
84562
  };
84451
84563
  var roundOne = (value) => Math.round(value * 10) / 10;
84564
+ var isFiniteNumber4 = (value) => typeof value === "number" && Number.isFinite(value);
84565
+ var averageFinite = (values) => {
84566
+ const finiteValues = values.filter(isFiniteNumber4);
84567
+ if (finiteValues.length === 0) return null;
84568
+ return finiteValues.reduce((sum, value) => sum + value, 0) / finiteValues.length;
84569
+ };
84570
+ var getEnabledAreaInfo2 = (row) => {
84571
+ const areaId = row.factory_area_id?.trim();
84572
+ const areaName = row.factory_area_name?.trim();
84573
+ if (!areaId || !areaName) return null;
84574
+ if (row.factory_area_enabled !== true) return null;
84575
+ return {
84576
+ areaId,
84577
+ areaName,
84578
+ factoryId: row.factory_id?.trim() || null
84579
+ };
84580
+ };
84452
84581
  var formatIdleDuration = (seconds) => {
84453
84582
  if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
84454
84583
  return "--";
@@ -85183,11 +85312,49 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85183
85312
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
85184
85313
  const mergedPoorestLines = React144__namespace.default.useMemo(() => {
85185
85314
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
85186
- return rows.slice(0, 3).map((line) => {
85315
+ const lineRows = [];
85316
+ const areaGroups = /* @__PURE__ */ new Map();
85317
+ rows.forEach((line) => {
85318
+ if (line.row_type === "area") {
85319
+ const efficiency = toNumber4(line.avg_efficiency);
85320
+ if (efficiency === null) return;
85321
+ const previousEfficiency = toNumber4(line.previous_avg_efficiency);
85322
+ const lineCount = Math.max(1, Math.round(toNumber4(line.line_count) || 1));
85323
+ lineRows.push({
85324
+ rowType: "area",
85325
+ id: line.line_id || `area:${line.factory_area_id || line.line_name || "unknown"}`,
85326
+ name: line.factory_area_name?.trim() || line.line_name?.trim() || "Unknown Area",
85327
+ efficiency: roundOne(efficiency),
85328
+ previousEfficiency: previousEfficiency === null ? null : roundOne(previousEfficiency),
85329
+ delta: toNumber4(line.delta_pp) ?? (previousEfficiency === null ? null : roundOne(efficiency - previousEfficiency)),
85330
+ supervisor: `${lineCount} ${lineCount === 1 ? "line" : "lines"}`,
85331
+ supervisorImage: null,
85332
+ lineCount,
85333
+ factoryId: line.factory_id ?? null,
85334
+ factoryAreaId: line.factory_area_id ?? null
85335
+ });
85336
+ return;
85337
+ }
85338
+ const area = getEnabledAreaInfo2(line);
85339
+ if (area) {
85340
+ const group = areaGroups.get(area.areaId);
85341
+ if (group) {
85342
+ group.lines.push(line);
85343
+ } else {
85344
+ areaGroups.set(area.areaId, {
85345
+ areaId: area.areaId,
85346
+ areaName: area.areaName,
85347
+ factoryId: area.factoryId,
85348
+ lines: [line]
85349
+ });
85350
+ }
85351
+ return;
85352
+ }
85187
85353
  const lineId = line.line_id || "";
85188
85354
  const supervisors = supervisorsByLineId.get(lineId) || [];
85189
85355
  const supervisor = supervisors[0];
85190
- return {
85356
+ lineRows.push({
85357
+ rowType: "line",
85191
85358
  id: lineId,
85192
85359
  name: line.line_name?.trim() || "Unknown Line",
85193
85360
  efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
@@ -85195,8 +85362,31 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85195
85362
  delta: toNumber4(line.delta_pp),
85196
85363
  supervisor: supervisor?.displayName || "Unassigned",
85197
85364
  supervisorImage: supervisor?.profilePhotoUrl ?? null
85198
- };
85365
+ });
85366
+ });
85367
+ areaGroups.forEach((group) => {
85368
+ const efficiency = averageFinite(group.lines.map((line) => toNumber4(line.avg_efficiency)));
85369
+ if (efficiency === null) return;
85370
+ const previousEfficiency = averageFinite(group.lines.map((line) => toNumber4(line.previous_avg_efficiency)));
85371
+ const lineCount = group.lines.length;
85372
+ lineRows.push({
85373
+ rowType: "area",
85374
+ id: `area:${group.areaId}`,
85375
+ name: group.areaName,
85376
+ efficiency: roundOne(efficiency),
85377
+ previousEfficiency: previousEfficiency === null ? null : roundOne(previousEfficiency),
85378
+ delta: previousEfficiency === null ? null : roundOne(efficiency - previousEfficiency),
85379
+ supervisor: `${lineCount} ${lineCount === 1 ? "line" : "lines"}`,
85380
+ supervisorImage: null,
85381
+ lineCount,
85382
+ factoryId: group.factoryId,
85383
+ factoryAreaId: group.areaId
85384
+ });
85199
85385
  });
85386
+ return lineRows.sort((left, right) => {
85387
+ if (left.efficiency !== right.efficiency) return left.efficiency - right.efficiency;
85388
+ return left.name.localeCompare(right.name, void 0, { sensitivity: "base", numeric: true });
85389
+ }).slice(0, 3);
85200
85390
  }, [poorestLineMode, snapshot.data.poorest_lines, supervisorsByLineId]);
85201
85391
  const showPoorestModeToggle = !!availableLineModes?.has_output && !!availableLineModes?.has_uptime;
85202
85392
  const poorestMetricLabel = poorestLineMode === "uptime" ? "Uptime" : "Efficiency";
@@ -85242,7 +85432,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85242
85432
  ] }),
85243
85433
  /* @__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: [
85244
85434
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2", children: [
85245
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider min-w-[120px]", children: "Line" }),
85435
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider min-w-[120px]", children: "Line / Area" }),
85246
85436
  /* @__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 })
85247
85437
  ] }),
85248
85438
  showSnapshotSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewListSkeleton, {}) : mergedPoorestLines.length > 0 ? mergedPoorestLines.map((line) => {
@@ -85250,7 +85440,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85250
85440
  return /* @__PURE__ */ jsxRuntime.jsx(
85251
85441
  "div",
85252
85442
  {
85253
- onClick: () => onLineClick(line.id, line.name),
85443
+ onClick: () => onLineClick(line),
85254
85444
  className: "block py-3 hover:bg-slate-50/50 transition-colors cursor-pointer group relative",
85255
85445
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4", children: [
85256
85446
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
@@ -85261,7 +85451,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85261
85451
  alt: line.supervisor,
85262
85452
  className: "w-full h-full object-cover"
85263
85453
  }
85264
- ) : /* @__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) }) }),
85454
+ ) : /* @__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) }) }),
85265
85455
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
85266
85456
  /* @__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 }) }),
85267
85457
  /* @__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 }) })
@@ -86551,18 +86741,33 @@ var PlantHeadView = () => {
86551
86741
  resolvedOperationalToday
86552
86742
  ]
86553
86743
  );
86554
- const handleOpenLineDetails = React144__namespace.default.useCallback((lineId, lineName) => {
86744
+ const handleOpenLineDetails = React144__namespace.default.useCallback((line) => {
86555
86745
  trackCoreEvent("Operations Overview Line Clicked", {
86556
- line_id: lineId,
86557
- line_name: lineName,
86746
+ line_id: line.rowType === "line" ? line.id : null,
86747
+ line_name: line.name,
86748
+ row_type: line.rowType,
86749
+ factory_id: line.factoryId ?? null,
86750
+ factory_area_id: line.factoryAreaId ?? null,
86558
86751
  range_start: dateRange.startKey,
86559
86752
  range_end: dateRange.endKey
86560
86753
  });
86754
+ if (line.rowType === "area" && line.factoryId && line.factoryAreaId) {
86755
+ const params = new URLSearchParams({
86756
+ factory_id: line.factoryId,
86757
+ factory_area_id: line.factoryAreaId
86758
+ });
86759
+ navigate(`/kpis?${params.toString()}`);
86760
+ return;
86761
+ }
86762
+ if (line.rowType === "area") {
86763
+ navigate("/kpis");
86764
+ return;
86765
+ }
86561
86766
  if (isLiveScope) {
86562
- navigate(`/kpis/${lineId}?returnTo=${encodeURIComponent("/")}`);
86767
+ navigate(`/kpis/${line.id}?returnTo=${encodeURIComponent("/")}`);
86563
86768
  return;
86564
86769
  }
86565
- navigate(buildLineMonthlyHistoryUrl(lineId));
86770
+ navigate(buildLineMonthlyHistoryUrl(line.id));
86566
86771
  }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, isLiveScope, navigate]);
86567
86772
  useOperationsOverviewRefresh({
86568
86773
  store,
package/dist/index.mjs CHANGED
@@ -14722,6 +14722,7 @@ var transformMonitorWorkspaceMetrics = ({
14722
14722
  video_grid_green_streak_minutes: item.video_grid_green_streak_minutes ?? null,
14723
14723
  video_grid_green_streak_anchor_at: item.video_grid_green_streak_anchor_at ?? null,
14724
14724
  video_grid_green_streak_started_at: item.video_grid_green_streak_started_at ?? null,
14725
+ video_grid_green_streak_observed_at: item.video_grid_green_streak_observed_at ?? null,
14725
14726
  scheduled_break_active: item.scheduled_break_active ?? false,
14726
14727
  incoming_wip_current: item.incoming_wip_current ?? null,
14727
14728
  incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
@@ -37994,17 +37995,22 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
37994
37995
  const activeStreaks = visibleWorkspaces.map((workspace) => {
37995
37996
  const startedAt = workspace.video_grid_green_streak_started_at;
37996
37997
  const anchorAt = workspace.video_grid_green_streak_anchor_at;
37998
+ const observedAt = workspace.video_grid_green_streak_observed_at;
37997
37999
  const streakMinutes = workspace.video_grid_green_streak_minutes;
37998
38000
  const startedAtMs = parseTimestampMs(startedAt);
37999
38001
  const anchorAtMs = parseTimestampMs(anchorAt);
38002
+ const observedAtMs = parseTimestampMs(observedAt);
38000
38003
  const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs2);
38001
- const tickOriginAtMs = anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
38004
+ const tickOriginAtMs = Number.isFinite(observedAtMs) ? observedAtMs : anchorAtMs + CONFIRMED_MINUTE_DURATION_MS;
38005
+ const confirmedSeconds2 = Math.max(0, Math.floor((streakMinutes || 0) * 60));
38006
+ const visibleSeconds = confirmedSeconds2 + Math.max(0, Math.floor((nowMs2 - tickOriginAtMs) / 1e3));
38002
38007
  if (workspace.video_grid_green_streak_active === true && isFiniteNumber3(streakMinutes) && streakMinutes > 0 && startedAt && anchorAt && Number.isFinite(startedAtMs) && isFresh) {
38003
38008
  return {
38004
38009
  startedAt,
38005
38010
  startedAtMs,
38006
38011
  anchorAt,
38007
38012
  streakMinutes,
38013
+ visibleSeconds,
38008
38014
  tickOriginAtMs
38009
38015
  };
38010
38016
  }
@@ -38013,7 +38019,7 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
38013
38019
  if (activeStreaks.some((streak) => streak === null)) {
38014
38020
  return null;
38015
38021
  }
38016
- const strictestStartedAt = activeStreaks.reduce((latest, current) => current.streakMinutes < latest.streakMinutes || current.streakMinutes === latest.streakMinutes && current.startedAtMs > latest.startedAtMs ? current : latest);
38022
+ const strictestStartedAt = activeStreaks.reduce((latest, current) => current.visibleSeconds < latest.visibleSeconds || current.visibleSeconds === latest.visibleSeconds && current.startedAtMs > latest.startedAtMs ? current : latest);
38017
38023
  const confirmedSeconds = Math.max(0, Math.floor(strictestStartedAt.streakMinutes * 60));
38018
38024
  return {
38019
38025
  label: "All green",
@@ -38046,6 +38052,54 @@ var getStatusBadgeSignature = (workspace) => {
38046
38052
  badge.title
38047
38053
  ].join(":")).join("|");
38048
38054
  };
38055
+ var VALID_STATUS_BADGE_ICON_TOKENS = /* @__PURE__ */ new Set([
38056
+ "alert-triangle",
38057
+ "refresh-cw",
38058
+ "package",
38059
+ "clock",
38060
+ "user-x",
38061
+ "wrench",
38062
+ "activity",
38063
+ "clipboard-x"
38064
+ ]);
38065
+ var normalizeStatusBadgeToken = (value) => String(value || "").trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
38066
+ var TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS = /* @__PURE__ */ new Set([
38067
+ "machine_maintenance",
38068
+ "machine_downtime",
38069
+ "no_material"
38070
+ ]);
38071
+ var shouldRenderVideoGridStatusBadge = (badge) => {
38072
+ if (badge.kind !== "idle_reason") {
38073
+ return true;
38074
+ }
38075
+ const reasonTokens = [
38076
+ normalizeStatusBadgeToken(badge.label),
38077
+ normalizeStatusBadgeToken(badge.display_name),
38078
+ normalizeStatusBadgeToken(badge.title)
38079
+ ];
38080
+ return !reasonTokens.some((token) => TEMPORARILY_DISABLED_STATUS_BADGE_TOKENS.has(token));
38081
+ };
38082
+ var resolveVideoGridStatusBadgeIconToken = (badge) => {
38083
+ if (badge.kind === "no_plan") {
38084
+ return "clipboard-x";
38085
+ }
38086
+ const reasonTokens = /* @__PURE__ */ new Set([
38087
+ normalizeStatusBadgeToken(badge.label),
38088
+ normalizeStatusBadgeToken(badge.display_name),
38089
+ normalizeStatusBadgeToken(badge.title)
38090
+ ]);
38091
+ if (reasonTokens.has("no_material")) {
38092
+ return "package";
38093
+ }
38094
+ if (reasonTokens.has("machine_maintenance") || reasonTokens.has("machine_downtime")) {
38095
+ return "wrench";
38096
+ }
38097
+ const iconToken = normalizeStatusBadgeToken(badge.icon_token);
38098
+ if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {
38099
+ return iconToken;
38100
+ }
38101
+ return "activity";
38102
+ };
38049
38103
  function getTrendArrowAndColor(trend) {
38050
38104
  if (trend > 0) {
38051
38105
  return { arrow: "\u2191", color: "text-green-400" };
@@ -38104,7 +38158,7 @@ var VideoCard = React144__default.memo(({
38104
38158
  const shouldRenderMetricBadge = hasDisplayMetric;
38105
38159
  const badgeTitle = isHighEfficiencyOverride ? `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%` : hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
38106
38160
  const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;
38107
- const statusBadges = workspace.video_grid_badges || [];
38161
+ const statusBadges = (workspace.video_grid_badges || []).filter(shouldRenderVideoGridStatusBadge);
38108
38162
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "blue" ? "bg-[#0EA5E9]/30" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
38109
38163
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "blue" ? "bg-[#0EA5E9]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
38110
38164
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "blue" ? "Best" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -38193,7 +38247,7 @@ var VideoCard = React144__default.memo(({
38193
38247
  label: badge.label,
38194
38248
  displayName: badge.display_name,
38195
38249
  paletteToken: badge.palette_token,
38196
- iconToken: badge.icon_token,
38250
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
38197
38251
  isKnown: badge.is_known
38198
38252
  });
38199
38253
  const Icon2 = presentation.Icon;
@@ -64900,6 +64954,8 @@ var EMPTY_LINE_IDS = [];
64900
64954
  var EMPTY_WORKSPACES = [];
64901
64955
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
64902
64956
  var ALL_GREEN_MILESTONE_DURATION_MS = 6e3;
64957
+ var ALL_GREEN_CELEBRATION_SEEN_PREFIX = "optifye:all-green-celebration:v1:";
64958
+ var ALL_GREEN_MILESTONE_SEEN_PREFIX = "optifye:all-green-milestone:v1:";
64903
64959
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
64904
64960
  const safeElapsedSeconds = Math.max(1, Math.floor(elapsedSeconds));
64905
64961
  const minutes = Math.floor(safeElapsedSeconds / 60);
@@ -64910,6 +64966,29 @@ var getAllGreenBackendVisibleSeconds = (confirmedSeconds, tickOriginAtMs, nowMs2
64910
64966
  const localSecondOffset = Math.max(0, Math.floor((nowMs2 - tickOriginAtMs) / 1e3));
64911
64967
  return confirmedSeconds + localSecondOffset;
64912
64968
  };
64969
+ var getSessionSeenValue = (key) => {
64970
+ if (typeof window === "undefined") {
64971
+ return null;
64972
+ }
64973
+ try {
64974
+ const storage = window.sessionStorage;
64975
+ return storage ? storage.getItem(key) : null;
64976
+ } catch {
64977
+ return null;
64978
+ }
64979
+ };
64980
+ var setSessionSeenValue = (key) => {
64981
+ if (typeof window === "undefined") {
64982
+ return;
64983
+ }
64984
+ try {
64985
+ const storage = window.sessionStorage;
64986
+ storage?.setItem(key, "1");
64987
+ } catch {
64988
+ }
64989
+ };
64990
+ var buildAllGreenCelebrationSeenKey = (identity) => `${ALL_GREEN_CELEBRATION_SEEN_PREFIX}${identity}`;
64991
+ var buildAllGreenMilestoneSeenKey = (identity, milestoneSeconds) => `${ALL_GREEN_MILESTONE_SEEN_PREFIX}${identity}:${milestoneSeconds}`;
64913
64992
  var LoadingPageCmp = LoadingPage_default;
64914
64993
  var LoadingOverlayCmp = LoadingOverlay_default;
64915
64994
  function HomeView({
@@ -65470,6 +65549,12 @@ function HomeView({
65470
65549
  if (milestoneTracking.identity !== allGreenMilestoneIdentity) {
65471
65550
  milestoneTracking.identity = allGreenMilestoneIdentity;
65472
65551
  milestoneTracking.lastMilestoneSeconds = currentMilestoneSeconds;
65552
+ if (currentMilestoneSeconds !== null) {
65553
+ setSessionSeenValue(buildAllGreenMilestoneSeenKey(
65554
+ allGreenMilestoneIdentity,
65555
+ currentMilestoneSeconds
65556
+ ));
65557
+ }
65473
65558
  dismissGreenStreakMilestoneBanner();
65474
65559
  return;
65475
65560
  }
@@ -65479,6 +65564,15 @@ function HomeView({
65479
65564
  }
65480
65565
  if ((milestoneTracking.lastMilestoneSeconds ?? 0) < currentMilestone.milestoneSeconds) {
65481
65566
  milestoneTracking.lastMilestoneSeconds = currentMilestone.milestoneSeconds;
65567
+ const milestoneSeenKey = buildAllGreenMilestoneSeenKey(
65568
+ allGreenMilestoneIdentity,
65569
+ currentMilestone.milestoneSeconds
65570
+ );
65571
+ if (getSessionSeenValue(milestoneSeenKey) === "1") {
65572
+ dismissGreenStreakMilestoneBanner();
65573
+ return;
65574
+ }
65575
+ setSessionSeenValue(milestoneSeenKey);
65482
65576
  triggerGreenStreakMilestoneBanner(currentMilestone);
65483
65577
  }
65484
65578
  }, [
@@ -65497,6 +65591,9 @@ function HomeView({
65497
65591
  if (transitionState.signature !== allGreenCelebrationSignature) {
65498
65592
  transitionState.signature = allGreenCelebrationSignature;
65499
65593
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65594
+ if (currentValidStreakIdentity) {
65595
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65596
+ }
65500
65597
  dismissAllGreenCelebration();
65501
65598
  return;
65502
65599
  }
@@ -65507,11 +65604,18 @@ function HomeView({
65507
65604
  }
65508
65605
  if (transitionState.previousValidStreakIdentity === null) {
65509
65606
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65607
+ const celebrationSeenKey = buildAllGreenCelebrationSeenKey(currentValidStreakIdentity);
65608
+ if (getSessionSeenValue(celebrationSeenKey) === "1") {
65609
+ dismissAllGreenCelebration();
65610
+ return;
65611
+ }
65612
+ setSessionSeenValue(celebrationSeenKey);
65510
65613
  triggerAllGreenCelebration();
65511
65614
  return;
65512
65615
  }
65513
65616
  if (transitionState.previousValidStreakIdentity !== currentValidStreakIdentity) {
65514
65617
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65618
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65515
65619
  dismissAllGreenCelebration();
65516
65620
  }
65517
65621
  }, [
@@ -84072,11 +84176,19 @@ var normalizeSummary = (value) => ({
84072
84176
  avg_idle_per_workstation: normalizeIdleMetric(value?.avg_idle_per_workstation)
84073
84177
  });
84074
84178
  var normalizeLineRow = (value) => ({
84179
+ row_type: value?.row_type ?? null,
84075
84180
  line_id: value?.line_id,
84076
84181
  line_name: value?.line_name?.trim() || value?.line_id || "",
84077
84182
  avg_efficiency: normalizeNumber(value?.avg_efficiency),
84078
84183
  previous_avg_efficiency: normalizeNumber(value?.previous_avg_efficiency),
84079
- delta_pp: normalizeNumber(value?.delta_pp)
84184
+ delta_pp: normalizeNumber(value?.delta_pp),
84185
+ factory_id: value?.factory_id ?? null,
84186
+ factory_area_id: value?.factory_area_id ?? null,
84187
+ factory_area_key: value?.factory_area_key ?? null,
84188
+ factory_area_name: value?.factory_area_name?.trim() || null,
84189
+ factory_area_sort_order: normalizeNumber(value?.factory_area_sort_order),
84190
+ factory_area_enabled: typeof value?.factory_area_enabled === "boolean" ? value.factory_area_enabled : null,
84191
+ line_count: normalizeNumber(value?.line_count)
84080
84192
  });
84081
84193
  var normalizePoorestLines = (value) => ({
84082
84194
  output: (value?.output || []).map((item) => normalizeLineRow(item)),
@@ -84420,6 +84532,23 @@ var toNumber4 = (value) => {
84420
84532
  return null;
84421
84533
  };
84422
84534
  var roundOne = (value) => Math.round(value * 10) / 10;
84535
+ var isFiniteNumber4 = (value) => typeof value === "number" && Number.isFinite(value);
84536
+ var averageFinite = (values) => {
84537
+ const finiteValues = values.filter(isFiniteNumber4);
84538
+ if (finiteValues.length === 0) return null;
84539
+ return finiteValues.reduce((sum, value) => sum + value, 0) / finiteValues.length;
84540
+ };
84541
+ var getEnabledAreaInfo2 = (row) => {
84542
+ const areaId = row.factory_area_id?.trim();
84543
+ const areaName = row.factory_area_name?.trim();
84544
+ if (!areaId || !areaName) return null;
84545
+ if (row.factory_area_enabled !== true) return null;
84546
+ return {
84547
+ areaId,
84548
+ areaName,
84549
+ factoryId: row.factory_id?.trim() || null
84550
+ };
84551
+ };
84423
84552
  var formatIdleDuration = (seconds) => {
84424
84553
  if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
84425
84554
  return "--";
@@ -85154,11 +85283,49 @@ var PoorestPerformersCard = React144__default.memo(({
85154
85283
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
85155
85284
  const mergedPoorestLines = React144__default.useMemo(() => {
85156
85285
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
85157
- return rows.slice(0, 3).map((line) => {
85286
+ const lineRows = [];
85287
+ const areaGroups = /* @__PURE__ */ new Map();
85288
+ rows.forEach((line) => {
85289
+ if (line.row_type === "area") {
85290
+ const efficiency = toNumber4(line.avg_efficiency);
85291
+ if (efficiency === null) return;
85292
+ const previousEfficiency = toNumber4(line.previous_avg_efficiency);
85293
+ const lineCount = Math.max(1, Math.round(toNumber4(line.line_count) || 1));
85294
+ lineRows.push({
85295
+ rowType: "area",
85296
+ id: line.line_id || `area:${line.factory_area_id || line.line_name || "unknown"}`,
85297
+ name: line.factory_area_name?.trim() || line.line_name?.trim() || "Unknown Area",
85298
+ efficiency: roundOne(efficiency),
85299
+ previousEfficiency: previousEfficiency === null ? null : roundOne(previousEfficiency),
85300
+ delta: toNumber4(line.delta_pp) ?? (previousEfficiency === null ? null : roundOne(efficiency - previousEfficiency)),
85301
+ supervisor: `${lineCount} ${lineCount === 1 ? "line" : "lines"}`,
85302
+ supervisorImage: null,
85303
+ lineCount,
85304
+ factoryId: line.factory_id ?? null,
85305
+ factoryAreaId: line.factory_area_id ?? null
85306
+ });
85307
+ return;
85308
+ }
85309
+ const area = getEnabledAreaInfo2(line);
85310
+ if (area) {
85311
+ const group = areaGroups.get(area.areaId);
85312
+ if (group) {
85313
+ group.lines.push(line);
85314
+ } else {
85315
+ areaGroups.set(area.areaId, {
85316
+ areaId: area.areaId,
85317
+ areaName: area.areaName,
85318
+ factoryId: area.factoryId,
85319
+ lines: [line]
85320
+ });
85321
+ }
85322
+ return;
85323
+ }
85158
85324
  const lineId = line.line_id || "";
85159
85325
  const supervisors = supervisorsByLineId.get(lineId) || [];
85160
85326
  const supervisor = supervisors[0];
85161
- return {
85327
+ lineRows.push({
85328
+ rowType: "line",
85162
85329
  id: lineId,
85163
85330
  name: line.line_name?.trim() || "Unknown Line",
85164
85331
  efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
@@ -85166,8 +85333,31 @@ var PoorestPerformersCard = React144__default.memo(({
85166
85333
  delta: toNumber4(line.delta_pp),
85167
85334
  supervisor: supervisor?.displayName || "Unassigned",
85168
85335
  supervisorImage: supervisor?.profilePhotoUrl ?? null
85169
- };
85336
+ });
85337
+ });
85338
+ areaGroups.forEach((group) => {
85339
+ const efficiency = averageFinite(group.lines.map((line) => toNumber4(line.avg_efficiency)));
85340
+ if (efficiency === null) return;
85341
+ const previousEfficiency = averageFinite(group.lines.map((line) => toNumber4(line.previous_avg_efficiency)));
85342
+ const lineCount = group.lines.length;
85343
+ lineRows.push({
85344
+ rowType: "area",
85345
+ id: `area:${group.areaId}`,
85346
+ name: group.areaName,
85347
+ efficiency: roundOne(efficiency),
85348
+ previousEfficiency: previousEfficiency === null ? null : roundOne(previousEfficiency),
85349
+ delta: previousEfficiency === null ? null : roundOne(efficiency - previousEfficiency),
85350
+ supervisor: `${lineCount} ${lineCount === 1 ? "line" : "lines"}`,
85351
+ supervisorImage: null,
85352
+ lineCount,
85353
+ factoryId: group.factoryId,
85354
+ factoryAreaId: group.areaId
85355
+ });
85170
85356
  });
85357
+ return lineRows.sort((left, right) => {
85358
+ if (left.efficiency !== right.efficiency) return left.efficiency - right.efficiency;
85359
+ return left.name.localeCompare(right.name, void 0, { sensitivity: "base", numeric: true });
85360
+ }).slice(0, 3);
85171
85361
  }, [poorestLineMode, snapshot.data.poorest_lines, supervisorsByLineId]);
85172
85362
  const showPoorestModeToggle = !!availableLineModes?.has_output && !!availableLineModes?.has_uptime;
85173
85363
  const poorestMetricLabel = poorestLineMode === "uptime" ? "Uptime" : "Efficiency";
@@ -85213,7 +85403,7 @@ var PoorestPerformersCard = React144__default.memo(({
85213
85403
  ] }),
85214
85404
  /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col p-0 overflow-auto", children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-slate-50 flex-1 px-5", children: [
85215
85405
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-2", children: [
85216
- /* @__PURE__ */ jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider min-w-[120px]", children: "Line" }),
85406
+ /* @__PURE__ */ jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider min-w-[120px]", children: "Line / Area" }),
85217
85407
  /* @__PURE__ */ 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 })
85218
85408
  ] }),
85219
85409
  showSnapshotSkeleton ? /* @__PURE__ */ jsx(OverviewListSkeleton, {}) : mergedPoorestLines.length > 0 ? mergedPoorestLines.map((line) => {
@@ -85221,7 +85411,7 @@ var PoorestPerformersCard = React144__default.memo(({
85221
85411
  return /* @__PURE__ */ jsx(
85222
85412
  "div",
85223
85413
  {
85224
- onClick: () => onLineClick(line.id, line.name),
85414
+ onClick: () => onLineClick(line),
85225
85415
  className: "block py-3 hover:bg-slate-50/50 transition-colors cursor-pointer group relative",
85226
85416
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
85227
85417
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
@@ -85232,7 +85422,7 @@ var PoorestPerformersCard = React144__default.memo(({
85232
85422
  alt: line.supervisor,
85233
85423
  className: "w-full h-full object-cover"
85234
85424
  }
85235
- ) : /* @__PURE__ */ 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) }) }),
85425
+ ) : /* @__PURE__ */ 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) }) }),
85236
85426
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
85237
85427
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx("div", { className: "font-bold text-slate-800 text-[13px] truncate group-hover:text-indigo-600 transition-colors", children: line.name }) }),
85238
85428
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 mt-0.5 flex-wrap", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] text-slate-400 font-medium", children: line.supervisor }) })
@@ -86522,18 +86712,33 @@ var PlantHeadView = () => {
86522
86712
  resolvedOperationalToday
86523
86713
  ]
86524
86714
  );
86525
- const handleOpenLineDetails = React144__default.useCallback((lineId, lineName) => {
86715
+ const handleOpenLineDetails = React144__default.useCallback((line) => {
86526
86716
  trackCoreEvent("Operations Overview Line Clicked", {
86527
- line_id: lineId,
86528
- line_name: lineName,
86717
+ line_id: line.rowType === "line" ? line.id : null,
86718
+ line_name: line.name,
86719
+ row_type: line.rowType,
86720
+ factory_id: line.factoryId ?? null,
86721
+ factory_area_id: line.factoryAreaId ?? null,
86529
86722
  range_start: dateRange.startKey,
86530
86723
  range_end: dateRange.endKey
86531
86724
  });
86725
+ if (line.rowType === "area" && line.factoryId && line.factoryAreaId) {
86726
+ const params = new URLSearchParams({
86727
+ factory_id: line.factoryId,
86728
+ factory_area_id: line.factoryAreaId
86729
+ });
86730
+ navigate(`/kpis?${params.toString()}`);
86731
+ return;
86732
+ }
86733
+ if (line.rowType === "area") {
86734
+ navigate("/kpis");
86735
+ return;
86736
+ }
86532
86737
  if (isLiveScope) {
86533
- navigate(`/kpis/${lineId}?returnTo=${encodeURIComponent("/")}`);
86738
+ navigate(`/kpis/${line.id}?returnTo=${encodeURIComponent("/")}`);
86534
86739
  return;
86535
86740
  }
86536
- navigate(buildLineMonthlyHistoryUrl(lineId));
86741
+ navigate(buildLineMonthlyHistoryUrl(line.id));
86537
86742
  }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, isLiveScope, navigate]);
86538
86743
  useOperationsOverviewRefresh({
86539
86744
  store,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.12.30",
3
+ "version": "6.12.32",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",