@optifye/dashboard-core 6.12.29 → 6.12.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { format, addDays, subMonths, endOfMonth, startOfMonth, endOfDay, eachDayOfInterval, getDay, isSameDay, isWithinInterval, startOfDay, parseISO, subDays, differenceInMinutes, addMinutes, addMonths, isValid, formatDistanceToNow, isToday, isFuture, isBefore } from 'date-fns';
2
2
  import { formatInTimeZone, fromZonedTime, toZonedTime } from 'date-fns-tz';
3
3
  import * as React144 from 'react';
4
- import React144__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, forwardRef, useImperativeHandle, useLayoutEffect, memo as memo$1, useContext, useSyncExternalStore, useId, Children, isValidElement, useInsertionEffect, startTransition, Fragment as Fragment$1, createElement, Component } from 'react';
4
+ import React144__default, { createContext, useRef, useId, useCallback, useState, useMemo, useEffect, forwardRef, useImperativeHandle, useLayoutEffect, memo as memo$1, useContext, useSyncExternalStore, Children, isValidElement, useInsertionEffect, startTransition, Fragment as Fragment$1, createElement, Component } from 'react';
5
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
6
6
  import { useRouter } from 'next/router';
7
7
  import { toast } from 'sonner';
@@ -10,7 +10,7 @@ import { EventEmitter } from 'events';
10
10
  import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
11
11
  import Hls, { Events, ErrorTypes } from 'hls.js';
12
12
  import useSWR from 'swr';
13
- import { Camera, AlertTriangle, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, Filter, X, Coffee, Plus, ArrowUp, ArrowDown, ArrowRight, CheckCircle2, ArrowLeft, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, XCircle, HelpCircle, Activity, Wrench, UserX, Package, RefreshCw, Palette, TrendingDown, FolderOpen, Folder, ArrowDownWideNarrow, Tag, Sliders, Layers, Search, Edit2, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Video, Copy, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, Film, MessageSquare, Menu, Send, Settings, LifeBuoy, EyeOff, Zap, Flame, Crown, Medal } from 'lucide-react';
13
+ import { Camera, AlertTriangle, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, Filter, X, Coffee, Plus, ArrowUp, ArrowDown, ArrowRight, HelpCircle, ClipboardX, Activity, Wrench, UserX, Clock, Package, RefreshCw, CheckCircle2, ArrowLeft, Calendar, Save, AlertCircle, Loader2, Minus, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, XCircle, Palette, TrendingDown, FolderOpen, Folder, ArrowDownWideNarrow, Tag, Sliders, Layers, Search, Edit2, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Video, Copy, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, Film, MessageSquare, Menu, Send, Settings, LifeBuoy, EyeOff, Zap, Flame, Crown, Medal } from 'lucide-react';
14
14
  import { memo, noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds } from 'motion-utils';
15
15
  import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, ReferenceLine, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, Customized, Cell, PieChart, Pie, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
16
16
  import { Slot } from '@radix-ui/react-slot';
@@ -4636,7 +4636,10 @@ var combineLineMetricsRows = (rows, dummySkuId) => {
4636
4636
  };
4637
4637
  const avgEfficiency = weighted("avg_efficiency");
4638
4638
  const avgCycleTime = weighted("avg_cycle_time");
4639
- const thresholdPph = weighted("threshold_pph");
4639
+ const thresholdPph = rowsForAggregation.reduce(
4640
+ (acc, row) => acc + (coerceOptionalNumber(row.threshold_pph) ?? 0),
4641
+ 0
4642
+ );
4640
4643
  const underperformingWorkspaces = activeRealRows.length > 0 ? roundHalfUpInt(
4641
4644
  activeRealRows.reduce(
4642
4645
  (acc, row) => acc + safeInt(row.underperforming_workspaces),
@@ -14687,6 +14690,7 @@ var transformMonitorWorkspaceMetrics = ({
14687
14690
  monitoring_mode: item.monitoring_mode ?? void 0,
14688
14691
  idle_time: idleTimeSeconds,
14689
14692
  idle_time_hourly: item.idle_time_hourly ?? null,
14693
+ idle_reason_hourly: item.idle_reason_hourly ?? null,
14690
14694
  shift_start: item.shift_start ?? void 0,
14691
14695
  shift_end: item.shift_end ?? void 0,
14692
14696
  assembly_enabled: item.assembly_enabled ?? false,
@@ -14718,10 +14722,12 @@ var transformMonitorWorkspaceMetrics = ({
14718
14722
  video_grid_green_streak_minutes: item.video_grid_green_streak_minutes ?? null,
14719
14723
  video_grid_green_streak_anchor_at: item.video_grid_green_streak_anchor_at ?? null,
14720
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,
14721
14726
  scheduled_break_active: item.scheduled_break_active ?? false,
14722
14727
  incoming_wip_current: item.incoming_wip_current ?? null,
14723
14728
  incoming_wip_effective_at: item.incoming_wip_effective_at ?? null,
14724
14729
  incoming_wip_buffer_name: item.incoming_wip_buffer_name ?? null,
14730
+ video_grid_badges: Array.isArray(item.video_grid_badges) ? item.video_grid_badges : [],
14725
14731
  show_exclamation: item.show_exclamation ?? void 0
14726
14732
  };
14727
14733
  workspaceMetricsStore.setOverview(metric);
@@ -21240,6 +21246,7 @@ var ICON_CONFIG = {
21240
21246
  "user-x": UserX,
21241
21247
  wrench: Wrench,
21242
21248
  activity: Activity,
21249
+ "clipboard-x": ClipboardX,
21243
21250
  "help-circle": HelpCircle
21244
21251
  };
21245
21252
  var humanizeIdleReasonLabel = (value) => {
@@ -37988,17 +37995,22 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
37988
37995
  const activeStreaks = visibleWorkspaces.map((workspace) => {
37989
37996
  const startedAt = workspace.video_grid_green_streak_started_at;
37990
37997
  const anchorAt = workspace.video_grid_green_streak_anchor_at;
37998
+ const observedAt = workspace.video_grid_green_streak_observed_at;
37991
37999
  const streakMinutes = workspace.video_grid_green_streak_minutes;
37992
38000
  const startedAtMs = parseTimestampMs(startedAt);
37993
38001
  const anchorAtMs = parseTimestampMs(anchorAt);
38002
+ const observedAtMs = parseTimestampMs(observedAt);
37994
38003
  const isFresh = isAllGreenStreakFresh(workspace, anchorAtMs, nowMs2);
37995
- 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));
37996
38007
  if (workspace.video_grid_green_streak_active === true && isFiniteNumber3(streakMinutes) && streakMinutes > 0 && startedAt && anchorAt && Number.isFinite(startedAtMs) && isFresh) {
37997
38008
  return {
37998
38009
  startedAt,
37999
38010
  startedAtMs,
38000
38011
  anchorAt,
38001
38012
  streakMinutes,
38013
+ visibleSeconds,
38002
38014
  tickOriginAtMs
38003
38015
  };
38004
38016
  }
@@ -38007,7 +38019,7 @@ var getAllVideoGridGreenStreakDisplay = (workspaces, legend = DEFAULT_EFFICIENCY
38007
38019
  if (activeStreaks.some((streak) => streak === null)) {
38008
38020
  return null;
38009
38021
  }
38010
- 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);
38011
38023
  const confirmedSeconds = Math.max(0, Math.floor(strictestStartedAt.streakMinutes * 60));
38012
38024
  return {
38013
38025
  label: "All green",
@@ -38026,6 +38038,52 @@ var getVideoGridLegendLabel = (workspaces) => {
38026
38038
  }
38027
38039
  return visibleWorkspaces.some(isVideoGridRecentFlowEnabled) ? VIDEO_GRID_LEGEND_LABEL : MAP_GRID_LEGEND_LABEL;
38028
38040
  };
38041
+ var getStatusBadgeSignature = (workspace) => {
38042
+ const badges = workspace.video_grid_badges || [];
38043
+ return badges.map((badge) => [
38044
+ badge.kind,
38045
+ badge.label,
38046
+ badge.display_name,
38047
+ badge.palette_token,
38048
+ badge.icon_token,
38049
+ badge.anchor_minute,
38050
+ badge.reason_minute,
38051
+ badge.shift_elapsed_fraction,
38052
+ badge.title
38053
+ ].join(":")).join("|");
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
+ };
38029
38087
  function getTrendArrowAndColor(trend) {
38030
38088
  if (trend > 0) {
38031
38089
  return { arrow: "\u2191", color: "text-green-400" };
@@ -38057,6 +38115,7 @@ var VideoCard = React144__default.memo(({
38057
38115
  }) => {
38058
38116
  const videoRef = useRef(null);
38059
38117
  const canvasRef = useRef(null);
38118
+ const statusBadgeIdPrefix = useId();
38060
38119
  const effectiveLegend = legend || DEFAULT_EFFICIENCY_LEGEND;
38061
38120
  const { isStale: isStreamStale } = useHlsStreamWithCropping(videoRef, canvasRef, {
38062
38121
  src: hlsUrl,
@@ -38083,6 +38142,7 @@ var VideoCard = React144__default.memo(({
38083
38142
  const shouldRenderMetricBadge = hasDisplayMetric;
38084
38143
  const badgeTitle = isHighEfficiencyOverride ? `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%` : hasVideoGridRecentFlow(workspace) ? `Flow ${Math.round(videoGridDisplayValue ?? 0)}%` : isRecentFlowCard ? "Flow unavailable" : `Efficiency ${Math.round(videoGridDisplayValue ?? 0)}%`;
38085
38144
  const badgeLabel = `${Math.round(videoGridDisplayValue ?? 0)}%`;
38145
+ const statusBadges = workspace.video_grid_badges || [];
38086
38146
  const efficiencyOverlayClass = videoGridColorState === "green" ? "bg-[#00D654]/25" : videoGridColorState === "blue" ? "bg-[#0EA5E9]/30" : videoGridColorState === "yellow" ? "bg-[#FFD700]/30" : videoGridColorState === "red" ? "bg-[#FF2D0A]/30" : "bg-transparent";
38087
38147
  const efficiencyBarClass = videoGridColorState === "green" ? "bg-[#00AB45]" : videoGridColorState === "blue" ? "bg-[#0EA5E9]" : videoGridColorState === "yellow" ? "bg-[#FFB020]" : videoGridColorState === "red" ? "bg-[#E34329]" : "bg-gray-500/70";
38088
38148
  const efficiencyStatus = videoGridColorState === "green" ? "High" : videoGridColorState === "blue" ? "Best" : videoGridColorState === "yellow" ? "Medium" : videoGridColorState === "red" ? "Low" : "Neutral";
@@ -38161,6 +38221,61 @@ var VideoCard = React144__default.memo(({
38161
38221
  children: /* @__PURE__ */ jsx("span", { className: `${compact ? "text-[10px]" : "text-xs"} font-semibold`, children: badgeLabel })
38162
38222
  }
38163
38223
  ) }),
38224
+ statusBadges.length > 0 && /* @__PURE__ */ jsx(
38225
+ "div",
38226
+ {
38227
+ "data-testid": "video-card-status-badges",
38228
+ className: `absolute ${compact ? "top-1.5 left-1.5 gap-1" : "top-2 left-2 gap-1.5"} z-30 flex items-center`,
38229
+ children: statusBadges.map((badge, index) => {
38230
+ const presentation = getIdleReasonPresentation({
38231
+ label: badge.label,
38232
+ displayName: badge.display_name,
38233
+ paletteToken: badge.palette_token,
38234
+ iconToken: resolveVideoGridStatusBadgeIconToken(badge),
38235
+ isKnown: badge.is_known
38236
+ });
38237
+ const Icon2 = presentation.Icon;
38238
+ const tooltipText = presentation.displayName;
38239
+ const tooltipId = `video-card-status-tooltip-${statusBadgeIdPrefix}-${badge.kind}-${index}`;
38240
+ return /* @__PURE__ */ jsxs(
38241
+ "div",
38242
+ {
38243
+ "data-testid": `video-card-status-badge-${badge.kind}`,
38244
+ "aria-label": tooltipText,
38245
+ "aria-describedby": tooltipId,
38246
+ className: `group relative inline-flex shrink-0 items-center justify-center rounded-full bg-slate-950/70 border-2 shadow-[0_3px_10px_rgba(0,0,0,0.34),inset_0_0_0_1px_rgba(255,255,255,0.18)] ${compact ? "h-10 w-10" : "h-11 w-11"}`,
38247
+ style: {
38248
+ borderColor: presentation.hex
38249
+ },
38250
+ children: [
38251
+ /* @__PURE__ */ jsx(
38252
+ Icon2,
38253
+ {
38254
+ "aria-hidden": "true",
38255
+ className: `${compact ? "h-5 w-5" : "h-6 w-6"} text-white`,
38256
+ strokeWidth: 2.4
38257
+ }
38258
+ ),
38259
+ /* @__PURE__ */ jsxs(
38260
+ "span",
38261
+ {
38262
+ id: tooltipId,
38263
+ role: "tooltip",
38264
+ "data-testid": `video-card-status-tooltip-${badge.kind}`,
38265
+ className: "pointer-events-none absolute left-0 top-full mt-2 whitespace-nowrap rounded-md border border-white/10 bg-slate-950/95 px-2 py-1 text-[11px] font-semibold leading-none text-white opacity-0 shadow-[0_6px_18px_rgba(0,0,0,0.34)] transition-opacity duration-150 group-hover:opacity-100",
38266
+ children: [
38267
+ /* @__PURE__ */ jsx("span", { className: "absolute -top-1 left-3 h-2 w-2 rotate-45 border-l border-t border-white/10 bg-slate-950/95" }),
38268
+ tooltipText
38269
+ ]
38270
+ }
38271
+ )
38272
+ ]
38273
+ },
38274
+ `${badge.kind}-${badge.label || index}-${badge.reason_minute ?? index}`
38275
+ );
38276
+ })
38277
+ }
38278
+ ),
38164
38279
  /* @__PURE__ */ jsx("div", { className: `absolute bottom-0 left-0 right-0 ${compact ? "h-0.5" : "h-1"} bg-black/50 z-30`, children: /* @__PURE__ */ jsx(
38165
38280
  "div",
38166
38281
  {
@@ -38197,7 +38312,7 @@ var VideoCard = React144__default.memo(({
38197
38312
  }
38198
38313
  );
38199
38314
  }, (prevProps, nextProps) => {
38200
- if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.recent_flow_forced_zero_after_shift !== nextProps.workspace.recent_flow_forced_zero_after_shift || prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
38315
+ if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.assembly_enabled !== nextProps.workspace.assembly_enabled || prevProps.workspace.video_grid_metric_mode !== nextProps.workspace.video_grid_metric_mode || prevProps.workspace.recent_flow_percent !== nextProps.workspace.recent_flow_percent || prevProps.workspace.recent_flow_effective_end_at !== nextProps.workspace.recent_flow_effective_end_at || prevProps.workspace.recent_flow_forced_zero_after_shift !== nextProps.workspace.recent_flow_forced_zero_after_shift || prevProps.workspace.scheduled_break_active !== nextProps.workspace.scheduled_break_active || prevProps.workspace.incoming_wip_current !== nextProps.workspace.incoming_wip_current || prevProps.workspace.incoming_wip_buffer_name !== nextProps.workspace.incoming_wip_buffer_name || getStatusBadgeSignature(prevProps.workspace) !== getStatusBadgeSignature(nextProps.workspace) || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
38201
38316
  return false;
38202
38317
  }
38203
38318
  if (prevProps.workspace.workspace_uuid !== nextProps.workspace.workspace_uuid || prevProps.workspace.workspace_name !== nextProps.workspace.workspace_name || prevProps.workspace.line_id !== nextProps.workspace.line_id) {
@@ -64823,6 +64938,8 @@ var EMPTY_LINE_IDS = [];
64823
64938
  var EMPTY_WORKSPACES = [];
64824
64939
  var ALL_GREEN_CELEBRATION_DURATION_MS = 6e3;
64825
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:";
64826
64943
  var formatAllGreenCelebrationTimer = (elapsedSeconds) => {
64827
64944
  const safeElapsedSeconds = Math.max(1, Math.floor(elapsedSeconds));
64828
64945
  const minutes = Math.floor(safeElapsedSeconds / 60);
@@ -64833,6 +64950,29 @@ var getAllGreenBackendVisibleSeconds = (confirmedSeconds, tickOriginAtMs, nowMs2
64833
64950
  const localSecondOffset = Math.max(0, Math.floor((nowMs2 - tickOriginAtMs) / 1e3));
64834
64951
  return confirmedSeconds + localSecondOffset;
64835
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}`;
64836
64976
  var LoadingPageCmp = LoadingPage_default;
64837
64977
  var LoadingOverlayCmp = LoadingOverlay_default;
64838
64978
  function HomeView({
@@ -65393,6 +65533,12 @@ function HomeView({
65393
65533
  if (milestoneTracking.identity !== allGreenMilestoneIdentity) {
65394
65534
  milestoneTracking.identity = allGreenMilestoneIdentity;
65395
65535
  milestoneTracking.lastMilestoneSeconds = currentMilestoneSeconds;
65536
+ if (currentMilestoneSeconds !== null) {
65537
+ setSessionSeenValue(buildAllGreenMilestoneSeenKey(
65538
+ allGreenMilestoneIdentity,
65539
+ currentMilestoneSeconds
65540
+ ));
65541
+ }
65396
65542
  dismissGreenStreakMilestoneBanner();
65397
65543
  return;
65398
65544
  }
@@ -65402,6 +65548,15 @@ function HomeView({
65402
65548
  }
65403
65549
  if ((milestoneTracking.lastMilestoneSeconds ?? 0) < currentMilestone.milestoneSeconds) {
65404
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);
65405
65560
  triggerGreenStreakMilestoneBanner(currentMilestone);
65406
65561
  }
65407
65562
  }, [
@@ -65420,6 +65575,9 @@ function HomeView({
65420
65575
  if (transitionState.signature !== allGreenCelebrationSignature) {
65421
65576
  transitionState.signature = allGreenCelebrationSignature;
65422
65577
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65578
+ if (currentValidStreakIdentity) {
65579
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65580
+ }
65423
65581
  dismissAllGreenCelebration();
65424
65582
  return;
65425
65583
  }
@@ -65430,11 +65588,18 @@ function HomeView({
65430
65588
  }
65431
65589
  if (transitionState.previousValidStreakIdentity === null) {
65432
65590
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65591
+ const celebrationSeenKey = buildAllGreenCelebrationSeenKey(currentValidStreakIdentity);
65592
+ if (getSessionSeenValue(celebrationSeenKey) === "1") {
65593
+ dismissAllGreenCelebration();
65594
+ return;
65595
+ }
65596
+ setSessionSeenValue(celebrationSeenKey);
65433
65597
  triggerAllGreenCelebration();
65434
65598
  return;
65435
65599
  }
65436
65600
  if (transitionState.previousValidStreakIdentity !== currentValidStreakIdentity) {
65437
65601
  transitionState.previousValidStreakIdentity = currentValidStreakIdentity;
65602
+ setSessionSeenValue(buildAllGreenCelebrationSeenKey(currentValidStreakIdentity));
65438
65603
  dismissAllGreenCelebration();
65439
65604
  }
65440
65605
  }, [
@@ -83995,11 +84160,19 @@ var normalizeSummary = (value) => ({
83995
84160
  avg_idle_per_workstation: normalizeIdleMetric(value?.avg_idle_per_workstation)
83996
84161
  });
83997
84162
  var normalizeLineRow = (value) => ({
84163
+ row_type: value?.row_type ?? null,
83998
84164
  line_id: value?.line_id,
83999
84165
  line_name: value?.line_name?.trim() || value?.line_id || "",
84000
84166
  avg_efficiency: normalizeNumber(value?.avg_efficiency),
84001
84167
  previous_avg_efficiency: normalizeNumber(value?.previous_avg_efficiency),
84002
- 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)
84003
84176
  });
84004
84177
  var normalizePoorestLines = (value) => ({
84005
84178
  output: (value?.output || []).map((item) => normalizeLineRow(item)),
@@ -84343,6 +84516,23 @@ var toNumber4 = (value) => {
84343
84516
  return null;
84344
84517
  };
84345
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
+ };
84346
84536
  var formatIdleDuration = (seconds) => {
84347
84537
  if (seconds === null || seconds === void 0 || !Number.isFinite(seconds)) {
84348
84538
  return "--";
@@ -85077,11 +85267,49 @@ var PoorestPerformersCard = React144__default.memo(({
85077
85267
  const showSnapshotSkeleton = snapshot.loading && !snapshot.hasLoadedOnce;
85078
85268
  const mergedPoorestLines = React144__default.useMemo(() => {
85079
85269
  const rows = snapshot.data.poorest_lines?.[poorestLineMode] || [];
85080
- 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
+ }
85081
85308
  const lineId = line.line_id || "";
85082
85309
  const supervisors = supervisorsByLineId.get(lineId) || [];
85083
85310
  const supervisor = supervisors[0];
85084
- return {
85311
+ lineRows.push({
85312
+ rowType: "line",
85085
85313
  id: lineId,
85086
85314
  name: line.line_name?.trim() || "Unknown Line",
85087
85315
  efficiency: roundOne(toNumber4(line.avg_efficiency) || 0),
@@ -85089,8 +85317,31 @@ var PoorestPerformersCard = React144__default.memo(({
85089
85317
  delta: toNumber4(line.delta_pp),
85090
85318
  supervisor: supervisor?.displayName || "Unassigned",
85091
85319
  supervisorImage: supervisor?.profilePhotoUrl ?? null
85092
- };
85320
+ });
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
+ });
85093
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);
85094
85345
  }, [poorestLineMode, snapshot.data.poorest_lines, supervisorsByLineId]);
85095
85346
  const showPoorestModeToggle = !!availableLineModes?.has_output && !!availableLineModes?.has_uptime;
85096
85347
  const poorestMetricLabel = poorestLineMode === "uptime" ? "Uptime" : "Efficiency";
@@ -85136,7 +85387,7 @@ var PoorestPerformersCard = React144__default.memo(({
85136
85387
  ] }),
85137
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: [
85138
85389
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-2", children: [
85139
- /* @__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" }),
85140
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 })
85141
85392
  ] }),
85142
85393
  showSnapshotSkeleton ? /* @__PURE__ */ jsx(OverviewListSkeleton, {}) : mergedPoorestLines.length > 0 ? mergedPoorestLines.map((line) => {
@@ -85144,7 +85395,7 @@ var PoorestPerformersCard = React144__default.memo(({
85144
85395
  return /* @__PURE__ */ jsx(
85145
85396
  "div",
85146
85397
  {
85147
- onClick: () => onLineClick(line.id, line.name),
85398
+ onClick: () => onLineClick(line),
85148
85399
  className: "block py-3 hover:bg-slate-50/50 transition-colors cursor-pointer group relative",
85149
85400
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
85150
85401
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
@@ -85155,7 +85406,7 @@ var PoorestPerformersCard = React144__default.memo(({
85155
85406
  alt: line.supervisor,
85156
85407
  className: "w-full h-full object-cover"
85157
85408
  }
85158
- ) : /* @__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) }) }),
85159
85410
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
85160
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 }) }),
85161
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 }) })
@@ -86445,18 +86696,33 @@ var PlantHeadView = () => {
86445
86696
  resolvedOperationalToday
86446
86697
  ]
86447
86698
  );
86448
- const handleOpenLineDetails = React144__default.useCallback((lineId, lineName) => {
86699
+ const handleOpenLineDetails = React144__default.useCallback((line) => {
86449
86700
  trackCoreEvent("Operations Overview Line Clicked", {
86450
- line_id: lineId,
86451
- 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,
86452
86706
  range_start: dateRange.startKey,
86453
86707
  range_end: dateRange.endKey
86454
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
+ }
86455
86721
  if (isLiveScope) {
86456
- navigate(`/kpis/${lineId}?returnTo=${encodeURIComponent("/")}`);
86722
+ navigate(`/kpis/${line.id}?returnTo=${encodeURIComponent("/")}`);
86457
86723
  return;
86458
86724
  }
86459
- navigate(buildLineMonthlyHistoryUrl(lineId));
86725
+ navigate(buildLineMonthlyHistoryUrl(line.id));
86460
86726
  }, [buildLineMonthlyHistoryUrl, dateRange.endKey, dateRange.startKey, isLiveScope, navigate]);
86461
86727
  useOperationsOverviewRefresh({
86462
86728
  store,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optifye/dashboard-core",
3
- "version": "6.12.29",
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",