@optifye/dashboard-core 6.12.30 → 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.
@@ -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,38 @@ 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 resolveVideoGridStatusBadgeIconToken = (badge) => {
1997
+ if (badge.kind === "no_plan") {
1998
+ return "clipboard-x";
1999
+ }
2000
+ const reasonTokens = /* @__PURE__ */ new Set([
2001
+ normalizeStatusBadgeToken(badge.label),
2002
+ normalizeStatusBadgeToken(badge.display_name),
2003
+ normalizeStatusBadgeToken(badge.title)
2004
+ ]);
2005
+ if (reasonTokens.has("no_material")) {
2006
+ return "package";
2007
+ }
2008
+ if (reasonTokens.has("machine_maintenance") || reasonTokens.has("machine_downtime")) {
2009
+ return "wrench";
2010
+ }
2011
+ const iconToken = normalizeStatusBadgeToken(badge.icon_token);
2012
+ if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {
2013
+ return iconToken;
2014
+ }
2015
+ return "activity";
2016
+ };
1985
2017
  function getTrendArrowAndColor(trend) {
1986
2018
  if (trend > 0) {
1987
2019
  return { arrow: "\u2191", color: "text-green-400" };
@@ -2129,7 +2161,7 @@ var VideoCard = React__default.default.memo(({
2129
2161
  label: badge.label,
2130
2162
  displayName: badge.display_name,
2131
2163
  paletteToken: badge.palette_token,
2132
- iconToken: badge.icon_token,
2164
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
2133
2165
  isKnown: badge.is_known
2134
2166
  });
2135
2167
  const Icon = presentation.Icon;
@@ -1975,6 +1975,38 @@ 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 resolveVideoGridStatusBadgeIconToken = (badge) => {
1990
+ if (badge.kind === "no_plan") {
1991
+ return "clipboard-x";
1992
+ }
1993
+ const reasonTokens = /* @__PURE__ */ new Set([
1994
+ normalizeStatusBadgeToken(badge.label),
1995
+ normalizeStatusBadgeToken(badge.display_name),
1996
+ normalizeStatusBadgeToken(badge.title)
1997
+ ]);
1998
+ if (reasonTokens.has("no_material")) {
1999
+ return "package";
2000
+ }
2001
+ if (reasonTokens.has("machine_maintenance") || reasonTokens.has("machine_downtime")) {
2002
+ return "wrench";
2003
+ }
2004
+ const iconToken = normalizeStatusBadgeToken(badge.icon_token);
2005
+ if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {
2006
+ return iconToken;
2007
+ }
2008
+ return "activity";
2009
+ };
1978
2010
  function getTrendArrowAndColor(trend) {
1979
2011
  if (trend > 0) {
1980
2012
  return { arrow: "\u2191", color: "text-green-400" };
@@ -2122,7 +2154,7 @@ var VideoCard = React.memo(({
2122
2154
  label: badge.label,
2123
2155
  displayName: badge.display_name,
2124
2156
  paletteToken: badge.palette_token,
2125
- iconToken: badge.icon_token,
2157
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
2126
2158
  isKnown: badge.is_known
2127
2159
  });
2128
2160
  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,38 @@ 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 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
+ };
38078
38116
  function getTrendArrowAndColor(trend) {
38079
38117
  if (trend > 0) {
38080
38118
  return { arrow: "\u2191", color: "text-green-400" };
@@ -38222,7 +38260,7 @@ var VideoCard = React144__namespace.default.memo(({
38222
38260
  label: badge.label,
38223
38261
  displayName: badge.display_name,
38224
38262
  paletteToken: badge.palette_token,
38225
- iconToken: badge.icon_token,
38263
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
38226
38264
  isKnown: badge.is_known
38227
38265
  });
38228
38266
  const Icon2 = presentation.Icon;
@@ -64929,6 +64967,8 @@ var EMPTY_LINE_IDS = [];
64929
64967
  var EMPTY_WORKSPACES = [];
64930
64968
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
64931
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:";
64932
64972
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
64933
64973
  const safeElapsedSeconds = Math.max(1, Math.floor(elapsedSeconds));
64934
64974
  const minutes = Math.floor(safeElapsedSeconds / 60);
@@ -64939,6 +64979,29 @@ var getAllGreenBackendVisibleSeconds = (confirmedSeconds, tickOriginAtMs, nowMs2
64939
64979
  const localSecondOffset = Math.max(0, Math.floor((nowMs2 - tickOriginAtMs) / 1e3));
64940
64980
  return confirmedSeconds + localSecondOffset;
64941
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}`;
64942
65005
  var LoadingPageCmp = LoadingPage_default;
64943
65006
  var LoadingOverlayCmp = LoadingOverlay_default;
64944
65007
  function HomeView({
@@ -65499,6 +65562,12 @@ function HomeView({
65499
65562
  if (milestoneTracking.identity !== allGreenMilestoneIdentity) {
65500
65563
  milestoneTracking.identity = allGreenMilestoneIdentity;
65501
65564
  milestoneTracking.lastMilestoneSeconds = currentMilestoneSeconds;
65565
+ if (currentMilestoneSeconds !== null) {
65566
+ setSessionSeenValue(buildAllGreenMilestoneSeenKey(
65567
+ allGreenMilestoneIdentity,
65568
+ currentMilestoneSeconds
65569
+ ));
65570
+ }
65502
65571
  dismissGreenStreakMilestoneBanner();
65503
65572
  return;
65504
65573
  }
@@ -65508,6 +65577,15 @@ function HomeView({
65508
65577
  }
65509
65578
  if ((milestoneTracking.lastMilestoneSeconds ?? 0) < currentMilestone.milestoneSeconds) {
65510
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);
65511
65589
  triggerGreenStreakMilestoneBanner(currentMilestone);
65512
65590
  }
65513
65591
  }, [
@@ -65526,6 +65604,9 @@ function HomeView({
65526
65604
  if (transitionState.signature !== allGreenCelebrationSignature) {
65527
65605
  transitionState.signature = allGreenCelebrationSignature;
65528
65606
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65607
+ if (currentValidStreakIdentity) {
65608
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65609
+ }
65529
65610
  dismissAllGreenCelebration();
65530
65611
  return;
65531
65612
  }
@@ -65536,11 +65617,18 @@ function HomeView({
65536
65617
  }
65537
65618
  if (transitionState.previousValidStreakIdentity === null) {
65538
65619
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65620
+ const celebrationSeenKey = buildAllGreenCelebrationSeenKey(currentValidStreakIdentity);
65621
+ if (getSessionSeenValue(celebrationSeenKey) === "1") {
65622
+ dismissAllGreenCelebration();
65623
+ return;
65624
+ }
65625
+ setSessionSeenValue(celebrationSeenKey);
65539
65626
  triggerAllGreenCelebration();
65540
65627
  return;
65541
65628
  }
65542
65629
  if (transitionState.previousValidStreakIdentity !== currentValidStreakIdentity) {
65543
65630
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65631
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65544
65632
  dismissAllGreenCelebration();
65545
65633
  }
65546
65634
  }, [
@@ -84101,11 +84189,19 @@ var normalizeSummary = (value) => ({
84101
84189
  avg_idle_per_workstation: normalizeIdleMetric(value?.avg_idle_per_workstation)
84102
84190
  });
84103
84191
  var normalizeLineRow = (value) => ({
84192
+ row_type: value?.row_type ?? null,
84104
84193
  line_id: value?.line_id,
84105
84194
  line_name: value?.line_name?.trim() || value?.line_id || "",
84106
84195
  avg_efficiency: normalizeNumber(value?.avg_efficiency),
84107
84196
  previous_avg_efficiency: normalizeNumber(value?.previous_avg_efficiency),
84108
- 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)
84109
84205
  });
84110
84206
  var normalizePoorestLines = (value) => ({
84111
84207
  output: (value?.output || []).map((item) => normalizeLineRow(item)),
@@ -84449,6 +84545,23 @@ var toNumber4 = (value) => {
84449
84545
  return null;
84450
84546
  };
84451
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
+ };
84452
84565
  var formatIdleDuration = (seconds) => {
84453
84566
  if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
84454
84567
  return "--";
@@ -85183,11 +85296,49 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85183
85296
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
85184
85297
  const mergedPoorestLines = React144__namespace.default.useMemo(() => {
85185
85298
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
85186
- 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
+ }
85187
85337
  const lineId = line.line_id || "";
85188
85338
  const supervisors = supervisorsByLineId.get(lineId) || [];
85189
85339
  const supervisor = supervisors[0];
85190
- return {
85340
+ lineRows.push({
85341
+ rowType: "line",
85191
85342
  id: lineId,
85192
85343
  name: line.line_name?.trim() || "Unknown Line",
85193
85344
  efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
@@ -85195,8 +85346,31 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85195
85346
  delta: toNumber4(line.delta_pp),
85196
85347
  supervisor: supervisor?.displayName || "Unassigned",
85197
85348
  supervisorImage: supervisor?.profilePhotoUrl ?? null
85198
- };
85349
+ });
85199
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
+ });
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);
85200
85374
  }, [poorestLineMode, snapshot.data.poorest_lines, supervisorsByLineId]);
85201
85375
  const showPoorestModeToggle = !!availableLineModes?.has_output && !!availableLineModes?.has_uptime;
85202
85376
  const poorestMetricLabel = poorestLineMode === "uptime" ? "Uptime" : "Efficiency";
@@ -85242,7 +85416,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85242
85416
  ] }),
85243
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: [
85244
85418
  /* @__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" }),
85419
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider min-w-[120px]", children: "Line / Area" }),
85246
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 })
85247
85421
  ] }),
85248
85422
  showSnapshotSkeleton ? /* @__PURE__ */ jsxRuntime.jsx(OverviewListSkeleton, {}) : mergedPoorestLines.length > 0 ? mergedPoorestLines.map((line) => {
@@ -85250,7 +85424,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85250
85424
  return /* @__PURE__ */ jsxRuntime.jsx(
85251
85425
  "div",
85252
85426
  {
85253
- onClick: () => onLineClick(line.id, line.name),
85427
+ onClick: () => onLineClick(line),
85254
85428
  className: "block py-3 hover:bg-slate-50/50 transition-colors cursor-pointer group relative",
85255
85429
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-4", children: [
85256
85430
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
@@ -85261,7 +85435,7 @@ var PoorestPerformersCard = React144__namespace.default.memo(({
85261
85435
  alt: line.supervisor,
85262
85436
  className: "w-full h-full object-cover"
85263
85437
  }
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) }) }),
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) }) }),
85265
85439
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
85266
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 }) }),
85267
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 }) })
@@ -86551,18 +86725,33 @@ var PlantHeadView = () => {
86551
86725
  resolvedOperationalToday
86552
86726
  ]
86553
86727
  );
86554
- const handleOpenLineDetails = React144__namespace.default.useCallback((lineId, lineName) => {
86728
+ const handleOpenLineDetails = React144__namespace.default.useCallback((line) => {
86555
86729
  trackCoreEvent("Operations Overview Line Clicked", {
86556
- line_id: lineId,
86557
- 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,
86558
86735
  range_start: dateRange.startKey,
86559
86736
  range_end: dateRange.endKey
86560
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
+ }
86561
86750
  if (isLiveScope) {
86562
- navigate(`/kpis/${lineId}?returnTo=${encodeURIComponent("/")}`);
86751
+ navigate(`/kpis/${line.id}?returnTo=${encodeURIComponent("/")}`);
86563
86752
  return;
86564
86753
  }
86565
- navigate(buildLineMonthlyHistoryUrl(lineId));
86754
+ navigate(buildLineMonthlyHistoryUrl(line.id));
86566
86755
  }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, isLiveScope, navigate]);
86567
86756
  useOperationsOverviewRefresh({
86568
86757
  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,38 @@ 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 resolveVideoGridStatusBadgeIconToken = (badge) => {
38067
+ if (badge.kind === "no_plan") {
38068
+ return "clipboard-x";
38069
+ }
38070
+ const reasonTokens = /* @__PURE__ */ new Set([
38071
+ normalizeStatusBadgeToken(badge.label),
38072
+ normalizeStatusBadgeToken(badge.display_name),
38073
+ normalizeStatusBadgeToken(badge.title)
38074
+ ]);
38075
+ if (reasonTokens.has("no_material")) {
38076
+ return "package";
38077
+ }
38078
+ if (reasonTokens.has("machine_maintenance") || reasonTokens.has("machine_downtime")) {
38079
+ return "wrench";
38080
+ }
38081
+ const iconToken = normalizeStatusBadgeToken(badge.icon_token);
38082
+ if (VALID_STATUS_BADGE_ICON_TOKENS.has(iconToken)) {
38083
+ return iconToken;
38084
+ }
38085
+ return "activity";
38086
+ };
38049
38087
  function getTrendArrowAndColor(trend) {
38050
38088
  if (trend > 0) {
38051
38089
  return { arrow: "\u2191", color: "text-green-400" };
@@ -38193,7 +38231,7 @@ var VideoCard = React144__default.memo(({
38193
38231
  label: badge.label,
38194
38232
  displayName: badge.display_name,
38195
38233
  paletteToken: badge.palette_token,
38196
- iconToken: badge.icon_token,
38234
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
38197
38235
  isKnown: badge.is_known
38198
38236
  });
38199
38237
  const Icon2 = presentation.Icon;
@@ -64900,6 +64938,8 @@ var EMPTY_LINE_IDS = [];
64900
64938
  var EMPTY_WORKSPACES = [];
64901
64939
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
64902
64940
  var ALL_GREEN_MILESTONE_DURATION_MS = 6e3;
64941
+ var ALL_GREEN_CELEBRATION_SEEN_PREFIX = "optifye:all-green-celebration:v1:";
64942
+ var ALL_GREEN_MILESTONE_SEEN_PREFIX = "optifye:all-green-milestone:v1:";
64903
64943
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
64904
64944
  const safeElapsedSeconds = Math.max(1, Math.floor(elapsedSeconds));
64905
64945
  const minutes = Math.floor(safeElapsedSeconds / 60);
@@ -64910,6 +64950,29 @@ var getAllGreenBackendVisibleSeconds = (confirmedSeconds, tickOriginAtMs, nowMs2
64910
64950
  const localSecondOffset = Math.max(0, Math.floor((nowMs2 - tickOriginAtMs) / 1e3));
64911
64951
  return confirmedSeconds + localSecondOffset;
64912
64952
  };
64953
+ var getSessionSeenValue = (key) => {
64954
+ if (typeof window === "undefined") {
64955
+ return null;
64956
+ }
64957
+ try {
64958
+ const storage = window.sessionStorage;
64959
+ return storage ? storage.getItem(key) : null;
64960
+ } catch {
64961
+ return null;
64962
+ }
64963
+ };
64964
+ var setSessionSeenValue = (key) => {
64965
+ if (typeof window === "undefined") {
64966
+ return;
64967
+ }
64968
+ try {
64969
+ const storage = window.sessionStorage;
64970
+ storage?.setItem(key, "1");
64971
+ } catch {
64972
+ }
64973
+ };
64974
+ var buildAllGreenCelebrationSeenKey = (identity) => `${ALL_GREEN_CELEBRATION_SEEN_PREFIX}${identity}`;
64975
+ var buildAllGreenMilestoneSeenKey = (identity, milestoneSeconds) => `${ALL_GREEN_MILESTONE_SEEN_PREFIX}${identity}:${milestoneSeconds}`;
64913
64976
  var LoadingPageCmp = LoadingPage_default;
64914
64977
  var LoadingOverlayCmp = LoadingOverlay_default;
64915
64978
  function HomeView({
@@ -65470,6 +65533,12 @@ function HomeView({
65470
65533
  if (milestoneTracking.identity !== allGreenMilestoneIdentity) {
65471
65534
  milestoneTracking.identity = allGreenMilestoneIdentity;
65472
65535
  milestoneTracking.lastMilestoneSeconds = currentMilestoneSeconds;
65536
+ if (currentMilestoneSeconds !== null) {
65537
+ setSessionSeenValue(buildAllGreenMilestoneSeenKey(
65538
+ allGreenMilestoneIdentity,
65539
+ currentMilestoneSeconds
65540
+ ));
65541
+ }
65473
65542
  dismissGreenStreakMilestoneBanner();
65474
65543
  return;
65475
65544
  }
@@ -65479,6 +65548,15 @@ function HomeView({
65479
65548
  }
65480
65549
  if ((milestoneTracking.lastMilestoneSeconds ?? 0) < currentMilestone.milestoneSeconds) {
65481
65550
  milestoneTracking.lastMilestoneSeconds = currentMilestone.milestoneSeconds;
65551
+ const milestoneSeenKey = buildAllGreenMilestoneSeenKey(
65552
+ allGreenMilestoneIdentity,
65553
+ currentMilestone.milestoneSeconds
65554
+ );
65555
+ if (getSessionSeenValue(milestoneSeenKey) === "1") {
65556
+ dismissGreenStreakMilestoneBanner();
65557
+ return;
65558
+ }
65559
+ setSessionSeenValue(milestoneSeenKey);
65482
65560
  triggerGreenStreakMilestoneBanner(currentMilestone);
65483
65561
  }
65484
65562
  }, [
@@ -65497,6 +65575,9 @@ function HomeView({
65497
65575
  if (transitionState.signature !== allGreenCelebrationSignature) {
65498
65576
  transitionState.signature = allGreenCelebrationSignature;
65499
65577
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65578
+ if (currentValidStreakIdentity) {
65579
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65580
+ }
65500
65581
  dismissAllGreenCelebration();
65501
65582
  return;
65502
65583
  }
@@ -65507,11 +65588,18 @@ function HomeView({
65507
65588
  }
65508
65589
  if (transitionState.previousValidStreakIdentity === null) {
65509
65590
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65591
+ const celebrationSeenKey = buildAllGreenCelebrationSeenKey(currentValidStreakIdentity);
65592
+ if (getSessionSeenValue(celebrationSeenKey) === "1") {
65593
+ dismissAllGreenCelebration();
65594
+ return;
65595
+ }
65596
+ setSessionSeenValue(celebrationSeenKey);
65510
65597
  triggerAllGreenCelebration();
65511
65598
  return;
65512
65599
  }
65513
65600
  if (transitionState.previousValidStreakIdentity !== currentValidStreakIdentity) {
65514
65601
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65602
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65515
65603
  dismissAllGreenCelebration();
65516
65604
  }
65517
65605
  }, [
@@ -84072,11 +84160,19 @@ var normalizeSummary = (value) => ({
84072
84160
  avg_idle_per_workstation: normalizeIdleMetric(value?.avg_idle_per_workstation)
84073
84161
  });
84074
84162
  var normalizeLineRow = (value) => ({
84163
+ row_type: value?.row_type ?? null,
84075
84164
  line_id: value?.line_id,
84076
84165
  line_name: value?.line_name?.trim() || value?.line_id || "",
84077
84166
  avg_efficiency: normalizeNumber(value?.avg_efficiency),
84078
84167
  previous_avg_efficiency: normalizeNumber(value?.previous_avg_efficiency),
84079
- delta_pp: normalizeNumber(value?.delta_pp)
84168
+ delta_pp: normalizeNumber(value?.delta_pp),
84169
+ factory_id: value?.factory_id ?? null,
84170
+ factory_area_id: value?.factory_area_id ?? null,
84171
+ factory_area_key: value?.factory_area_key ?? null,
84172
+ factory_area_name: value?.factory_area_name?.trim() || null,
84173
+ factory_area_sort_order: normalizeNumber(value?.factory_area_sort_order),
84174
+ factory_area_enabled: typeof value?.factory_area_enabled === "boolean" ? value.factory_area_enabled : null,
84175
+ line_count: normalizeNumber(value?.line_count)
84080
84176
  });
84081
84177
  var normalizePoorestLines = (value) => ({
84082
84178
  output: (value?.output || []).map((item) => normalizeLineRow(item)),
@@ -84420,6 +84516,23 @@ var toNumber4 = (value) => {
84420
84516
  return null;
84421
84517
  };
84422
84518
  var roundOne = (value) => Math.round(value * 10) / 10;
84519
+ var isFiniteNumber4 = (value) => typeof value === "number" && Number.isFinite(value);
84520
+ var averageFinite = (values) => {
84521
+ const finiteValues = values.filter(isFiniteNumber4);
84522
+ if (finiteValues.length === 0) return null;
84523
+ return finiteValues.reduce((sum, value) => sum + value, 0) / finiteValues.length;
84524
+ };
84525
+ var getEnabledAreaInfo2 = (row) => {
84526
+ const areaId = row.factory_area_id?.trim();
84527
+ const areaName = row.factory_area_name?.trim();
84528
+ if (!areaId || !areaName) return null;
84529
+ if (row.factory_area_enabled !== true) return null;
84530
+ return {
84531
+ areaId,
84532
+ areaName,
84533
+ factoryId: row.factory_id?.trim() || null
84534
+ };
84535
+ };
84423
84536
  var formatIdleDuration = (seconds) => {
84424
84537
  if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
84425
84538
  return "--";
@@ -85154,11 +85267,49 @@ var PoorestPerformersCard = React144__default.memo(({
85154
85267
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
85155
85268
  const mergedPoorestLines = React144__default.useMemo(() => {
85156
85269
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
85157
- return rows.slice(0, 3).map((line) => {
85270
+ const lineRows = [];
85271
+ const areaGroups = /* @__PURE__ */ new Map();
85272
+ rows.forEach((line) => {
85273
+ if (line.row_type === "area") {
85274
+ const efficiency = toNumber4(line.avg_efficiency);
85275
+ if (efficiency === null) return;
85276
+ const previousEfficiency = toNumber4(line.previous_avg_efficiency);
85277
+ const lineCount = Math.max(1, Math.round(toNumber4(line.line_count) || 1));
85278
+ lineRows.push({
85279
+ rowType: "area",
85280
+ id: line.line_id || `area:${line.factory_area_id || line.line_name || "unknown"}`,
85281
+ name: line.factory_area_name?.trim() || line.line_name?.trim() || "Unknown Area",
85282
+ efficiency: roundOne(efficiency),
85283
+ previousEfficiency: previousEfficiency === null ? null : roundOne(previousEfficiency),
85284
+ delta: toNumber4(line.delta_pp) ?? (previousEfficiency === null ? null : roundOne(efficiency - previousEfficiency)),
85285
+ supervisor: `${lineCount} ${lineCount === 1 ? "line" : "lines"}`,
85286
+ supervisorImage: null,
85287
+ lineCount,
85288
+ factoryId: line.factory_id ?? null,
85289
+ factoryAreaId: line.factory_area_id ?? null
85290
+ });
85291
+ return;
85292
+ }
85293
+ const area = getEnabledAreaInfo2(line);
85294
+ if (area) {
85295
+ const group = areaGroups.get(area.areaId);
85296
+ if (group) {
85297
+ group.lines.push(line);
85298
+ } else {
85299
+ areaGroups.set(area.areaId, {
85300
+ areaId: area.areaId,
85301
+ areaName: area.areaName,
85302
+ factoryId: area.factoryId,
85303
+ lines: [line]
85304
+ });
85305
+ }
85306
+ return;
85307
+ }
85158
85308
  const lineId = line.line_id || "";
85159
85309
  const supervisors = supervisorsByLineId.get(lineId) || [];
85160
85310
  const supervisor = supervisors[0];
85161
- return {
85311
+ lineRows.push({
85312
+ rowType: "line",
85162
85313
  id: lineId,
85163
85314
  name: line.line_name?.trim() || "Unknown Line",
85164
85315
  efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
@@ -85166,8 +85317,31 @@ var PoorestPerformersCard = React144__default.memo(({
85166
85317
  delta: toNumber4(line.delta_pp),
85167
85318
  supervisor: supervisor?.displayName || "Unassigned",
85168
85319
  supervisorImage: supervisor?.profilePhotoUrl ?? null
85169
- };
85320
+ });
85170
85321
  });
85322
+ areaGroups.forEach((group) => {
85323
+ const efficiency = averageFinite(group.lines.map((line) => toNumber4(line.avg_efficiency)));
85324
+ if (efficiency === null) return;
85325
+ const previousEfficiency = averageFinite(group.lines.map((line) => toNumber4(line.previous_avg_efficiency)));
85326
+ const lineCount = group.lines.length;
85327
+ lineRows.push({
85328
+ rowType: "area",
85329
+ id: `area:${group.areaId}`,
85330
+ name: group.areaName,
85331
+ efficiency: roundOne(efficiency),
85332
+ previousEfficiency: previousEfficiency === null ? null : roundOne(previousEfficiency),
85333
+ delta: previousEfficiency === null ? null : roundOne(efficiency - previousEfficiency),
85334
+ supervisor: `${lineCount} ${lineCount === 1 ? "line" : "lines"}`,
85335
+ supervisorImage: null,
85336
+ lineCount,
85337
+ factoryId: group.factoryId,
85338
+ factoryAreaId: group.areaId
85339
+ });
85340
+ });
85341
+ return lineRows.sort((left, right) => {
85342
+ if (left.efficiency !== right.efficiency) return left.efficiency - right.efficiency;
85343
+ return left.name.localeCompare(right.name, void 0, { sensitivity: "base", numeric: true });
85344
+ }).slice(0, 3);
85171
85345
  }, [poorestLineMode, snapshot.data.poorest_lines, supervisorsByLineId]);
85172
85346
  const showPoorestModeToggle = !!availableLineModes?.has_output && !!availableLineModes?.has_uptime;
85173
85347
  const poorestMetricLabel = poorestLineMode === "uptime" ? "Uptime" : "Efficiency";
@@ -85213,7 +85387,7 @@ var PoorestPerformersCard = React144__default.memo(({
85213
85387
  ] }),
85214
85388
  /* @__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
85389
  /* @__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" }),
85390
+ /* @__PURE__ */ jsx("div", { className: "font-semibold text-slate-400 text-[10px] uppercase tracking-wider min-w-[120px]", children: "Line / Area" }),
85217
85391
  /* @__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
85392
  ] }),
85219
85393
  showSnapshotSkeleton ? /* @__PURE__ */ jsx(OverviewListSkeleton, {}) : mergedPoorestLines.length > 0 ? mergedPoorestLines.map((line) => {
@@ -85221,7 +85395,7 @@ var PoorestPerformersCard = React144__default.memo(({
85221
85395
  return /* @__PURE__ */ jsx(
85222
85396
  "div",
85223
85397
  {
85224
- onClick: () => onLineClick(line.id, line.name),
85398
+ onClick: () => onLineClick(line),
85225
85399
  className: "block py-3 hover:bg-slate-50/50 transition-colors cursor-pointer group relative",
85226
85400
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
85227
85401
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
@@ -85232,7 +85406,7 @@ var PoorestPerformersCard = React144__default.memo(({
85232
85406
  alt: line.supervisor,
85233
85407
  className: "w-full h-full object-cover"
85234
85408
  }
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) }) }),
85409
+ ) : /* @__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
85410
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
85237
85411
  /* @__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
85412
  /* @__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 +86696,33 @@ var PlantHeadView = () => {
86522
86696
  resolvedOperationalToday
86523
86697
  ]
86524
86698
  );
86525
- const handleOpenLineDetails = React144__default.useCallback((lineId, lineName) => {
86699
+ const handleOpenLineDetails = React144__default.useCallback((line) => {
86526
86700
  trackCoreEvent("Operations Overview Line Clicked", {
86527
- line_id: lineId,
86528
- line_name: lineName,
86701
+ line_id: line.rowType === "line" ? line.id : null,
86702
+ line_name: line.name,
86703
+ row_type: line.rowType,
86704
+ factory_id: line.factoryId ?? null,
86705
+ factory_area_id: line.factoryAreaId ?? null,
86529
86706
  range_start: dateRange.startKey,
86530
86707
  range_end: dateRange.endKey
86531
86708
  });
86709
+ if (line.rowType === "area" && line.factoryId && line.factoryAreaId) {
86710
+ const params = new URLSearchParams({
86711
+ factory_id: line.factoryId,
86712
+ factory_area_id: line.factoryAreaId
86713
+ });
86714
+ navigate(`/kpis?${params.toString()}`);
86715
+ return;
86716
+ }
86717
+ if (line.rowType === "area") {
86718
+ navigate("/kpis");
86719
+ return;
86720
+ }
86532
86721
  if (isLiveScope) {
86533
- navigate(`/kpis/${lineId}?returnTo=${encodeURIComponent("/")}`);
86722
+ navigate(`/kpis/${line.id}?returnTo=${encodeURIComponent("/")}`);
86534
86723
  return;
86535
86724
  }
86536
- navigate(buildLineMonthlyHistoryUrl(lineId));
86725
+ navigate(buildLineMonthlyHistoryUrl(line.id));
86537
86726
  }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, isLiveScope, navigate]);
86538
86727
  useOperationsOverviewRefresh({
86539
86728
  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.31",
4
4
  "description": "Reusable UI & logic for Optifye dashboard",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",