@optifye/dashboard-core 6.12.51 → 6.12.53

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
@@ -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, RefreshCw, ArrowUpDown, X, Coffee, Plus, Filter, ArrowUp, ArrowDown, ArrowRight, HelpCircle, ClipboardX, Activity, Wrench, UserX, Clock, Package, Monitor, CheckCircle2, ArrowLeft, Calendar, Save, AlertCircle, Loader2, Minus, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, XCircle, Palette, LockKeyhole, TrendingDown, FolderOpen, Folder, ArrowDownWideNarrow, Tag, Sliders, Layers, Search, Edit2, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Video, Copy, Lightbulb, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, Film, MessageSquare, Menu, Send, Settings, LifeBuoy, EyeOff, Zap, ExternalLink, Flame, Crown, Medal } from 'lucide-react';
13
+ import { Camera, AlertTriangle, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, RefreshCw, ArrowUpDown, X, Coffee, Plus, Filter, ArrowUp, ArrowDown, ArrowRight, HelpCircle, ClipboardX, Activity, Wrench, UserX, Clock, Package, Monitor, CheckCircle2, ArrowLeft, Calendar, Save, AlertCircle, Loader2, Minus, ChevronLeft, ChevronRight, TrendingUp, Sparkles, Pause, Play, Palette, LockKeyhole, XCircle, Search, Edit2, TrendingDown, FolderOpen, Folder, ArrowDownWideNarrow, Tag, Sliders, Layers, CheckCircle, User, Users, Shield, Building2, Mail, Lock, Info, Share2, Trophy, Target, Download, Video, Copy, Lightbulb, Sun, Moon, MousePointer, UserPlus, UserCog, Trash2, Eye, MoreVertical, BarChart3, Pencil, UserCheck, LogOut, Film, MessageSquare, Menu, Send, Settings, LifeBuoy, EyeOff, Zap, ExternalLink, 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';
@@ -6038,7 +6038,8 @@ var getAuthToken2 = async () => {
6038
6038
  var workspaceService = {
6039
6039
  // Cache for workspace display names to avoid repeated API calls
6040
6040
  _workspaceDisplayNamesCache: /* @__PURE__ */ new Map(),
6041
- _cacheTimestamp: 0,
6041
+ _workspaceDisplayNamesInFlight: /* @__PURE__ */ new Map(),
6042
+ _workspaceDisplayNamesByLineInFlight: /* @__PURE__ */ new Map(),
6042
6043
  _cacheExpiryMs: 5 * 60 * 1e3,
6043
6044
  // 5 minutes cache
6044
6045
  // Cache for workspace lists to avoid repeated API calls (line configuration changes infrequently)
@@ -6297,12 +6298,74 @@ var workspaceService = {
6297
6298
  * Returns a map of workspace_id -> display_name
6298
6299
  */
6299
6300
  async getWorkspaceDisplayNames(companyId, lineId) {
6300
- try {
6301
+ const cacheKey = `${companyId || "all"}::${lineId || "all"}`;
6302
+ const existingInFlight = this._workspaceDisplayNamesInFlight.get(cacheKey);
6303
+ if (existingInFlight) {
6304
+ return existingInFlight;
6305
+ }
6306
+ const fetchPromise = (async () => {
6307
+ try {
6308
+ const token = await getAuthToken2();
6309
+ const apiUrl = getBackendUrl2();
6310
+ const params = new URLSearchParams();
6311
+ if (companyId) params.append("company_id", companyId);
6312
+ if (lineId) params.append("line_id", lineId);
6313
+ const response = await fetch(`${apiUrl}/api/workspaces/display-names?${params.toString()}`, {
6314
+ headers: {
6315
+ "Authorization": `Bearer ${token}`,
6316
+ "Content-Type": "application/json"
6317
+ }
6318
+ });
6319
+ if (!response.ok) {
6320
+ const errorText = await response.text();
6321
+ throw new Error(`Backend API error (${response.status}): ${errorText}`);
6322
+ }
6323
+ const data = await response.json();
6324
+ const displayNamesMap = /* @__PURE__ */ new Map();
6325
+ if (data.display_names) {
6326
+ Object.entries(data.display_names).forEach(([key, value]) => {
6327
+ displayNamesMap.set(key, value);
6328
+ });
6329
+ }
6330
+ this._workspaceDisplayNamesCache.set(cacheKey, {
6331
+ displayNames: displayNamesMap,
6332
+ timestamp: Date.now()
6333
+ });
6334
+ return displayNamesMap;
6335
+ } catch (error) {
6336
+ console.error("Error fetching workspace display names:", error);
6337
+ addSentryBreadcrumb("Workspace display-name fallback failed", {
6338
+ surface: "workspace_display_names",
6339
+ route: "/api/workspaces/display-names",
6340
+ severity: "warning",
6341
+ extras: {
6342
+ company_id: companyId || null,
6343
+ line_id: lineId || null,
6344
+ pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
6345
+ error_message: error instanceof Error ? error.message : String(error)
6346
+ }
6347
+ });
6348
+ throw error;
6349
+ } finally {
6350
+ this._workspaceDisplayNamesInFlight.delete(cacheKey);
6351
+ }
6352
+ })();
6353
+ this._workspaceDisplayNamesInFlight.set(cacheKey, fetchPromise);
6354
+ return fetchPromise;
6355
+ },
6356
+ async getWorkspaceDisplayNamesByLine(companyId, lineIds) {
6357
+ const normalizedLineIds = Array.from(new Set((lineIds || []).filter(Boolean))).sort();
6358
+ const cacheKey = `${companyId || "all"}::byLine::${normalizedLineIds.join(",") || "all"}`;
6359
+ const existingInFlight = this._workspaceDisplayNamesByLineInFlight.get(cacheKey);
6360
+ if (existingInFlight) {
6361
+ return existingInFlight;
6362
+ }
6363
+ const fetchPromise = (async () => {
6301
6364
  const token = await getAuthToken2();
6302
6365
  const apiUrl = getBackendUrl2();
6303
6366
  const params = new URLSearchParams();
6304
6367
  if (companyId) params.append("company_id", companyId);
6305
- if (lineId) params.append("line_id", lineId);
6368
+ if (normalizedLineIds.length > 0) params.append("line_ids", normalizedLineIds.join(","));
6306
6369
  const response = await fetch(`${apiUrl}/api/workspaces/display-names?${params.toString()}`, {
6307
6370
  headers: {
6308
6371
  "Authorization": `Bearer ${token}`,
@@ -6314,39 +6377,32 @@ var workspaceService = {
6314
6377
  throw new Error(`Backend API error (${response.status}): ${errorText}`);
6315
6378
  }
6316
6379
  const data = await response.json();
6317
- const displayNamesMap = /* @__PURE__ */ new Map();
6318
- if (data.display_names) {
6319
- Object.entries(data.display_names).forEach(([key, value]) => {
6320
- displayNamesMap.set(key, value);
6380
+ const source = data.display_names_by_line || {};
6381
+ const byLine = /* @__PURE__ */ new Map();
6382
+ Object.entries(source).forEach(([lineId, displayNames]) => {
6383
+ if (normalizedLineIds.length > 0 && !normalizedLineIds.includes(lineId)) return;
6384
+ const lineMap = /* @__PURE__ */ new Map();
6385
+ Object.entries(displayNames).forEach(([workspaceId, displayName]) => {
6386
+ lineMap.set(workspaceId, displayName);
6321
6387
  });
6322
- }
6323
- this._workspaceDisplayNamesCache = displayNamesMap;
6324
- this._cacheTimestamp = Date.now();
6325
- return displayNamesMap;
6326
- } catch (error) {
6327
- console.error("Error fetching workspace display names:", error);
6328
- addSentryBreadcrumb("Workspace display-name fallback failed", {
6329
- surface: "workspace_display_names",
6330
- route: "/api/workspaces/display-names",
6331
- severity: "warning",
6332
- extras: {
6333
- company_id: companyId || null,
6334
- line_id: lineId || null,
6335
- pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
6336
- error_message: error instanceof Error ? error.message : String(error)
6337
- }
6388
+ byLine.set(lineId, lineMap);
6338
6389
  });
6339
- throw error;
6340
- }
6390
+ return byLine;
6391
+ })().finally(() => {
6392
+ this._workspaceDisplayNamesByLineInFlight.delete(cacheKey);
6393
+ });
6394
+ this._workspaceDisplayNamesByLineInFlight.set(cacheKey, fetchPromise);
6395
+ return fetchPromise;
6341
6396
  },
6342
6397
  /**
6343
6398
  * Gets cached workspace display names (with cache expiry)
6344
6399
  */
6345
6400
  async getCachedWorkspaceDisplayNames(companyId, lineId) {
6346
6401
  const now4 = Date.now();
6347
- const cacheAge = now4 - this._cacheTimestamp;
6348
- if (cacheAge < this._cacheExpiryMs && this._workspaceDisplayNamesCache.size > 0) {
6349
- return this._workspaceDisplayNamesCache;
6402
+ const cacheKey = `${companyId || "all"}::${lineId || "all"}`;
6403
+ const cached = this._workspaceDisplayNamesCache.get(cacheKey);
6404
+ if (cached && now4 - cached.timestamp < this._cacheExpiryMs && cached.displayNames.size > 0) {
6405
+ return cached.displayNames;
6350
6406
  }
6351
6407
  return this.getWorkspaceDisplayNames(companyId, lineId);
6352
6408
  },
@@ -6367,7 +6423,8 @@ var workspaceService = {
6367
6423
  */
6368
6424
  clearWorkspaceDisplayNamesCache() {
6369
6425
  this._workspaceDisplayNamesCache.clear();
6370
- this._cacheTimestamp = 0;
6426
+ this._workspaceDisplayNamesInFlight.clear();
6427
+ this._workspaceDisplayNamesByLineInFlight.clear();
6371
6428
  },
6372
6429
  clearWorkspacesCache() {
6373
6430
  this._workspacesCache.clear();
@@ -6873,10 +6930,16 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
6873
6930
  hasBackendUrl() {
6874
6931
  return Boolean(process.env.NEXT_PUBLIC_BACKEND_URL);
6875
6932
  }
6933
+ isCanonicalDate(value) {
6934
+ return typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
6935
+ }
6876
6936
  async fetchBackendWorkspaceUptimeSummaries(companyId, lineIds, date, shiftId, timezone) {
6877
6937
  if (!this.hasBackendUrl()) {
6878
6938
  return null;
6879
6939
  }
6940
+ if (!this.isCanonicalDate(date)) {
6941
+ return null;
6942
+ }
6880
6943
  const supabase = _getSupabaseInstance();
6881
6944
  if (!supabase) throw new Error("Supabase client not initialized");
6882
6945
  const params = new URLSearchParams({
@@ -6926,6 +6989,9 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
6926
6989
  if (!this.hasBackendUrl()) {
6927
6990
  return null;
6928
6991
  }
6992
+ if (!this.isCanonicalDate(date)) {
6993
+ return null;
6994
+ }
6929
6995
  const supabase = _getSupabaseInstance();
6930
6996
  if (!supabase) throw new Error("Supabase client not initialized");
6931
6997
  const params = new URLSearchParams({
@@ -7660,6 +7726,24 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
7660
7726
  const currentTiming = this.getShiftTiming(timezone, shiftConfig);
7661
7727
  const queryDate = overrideDate ?? currentTiming.date;
7662
7728
  const queryShiftId = overrideShiftId ?? currentTiming.shiftId;
7729
+ if (!this.isCanonicalDate(queryDate)) {
7730
+ return {
7731
+ shiftId: queryShiftId,
7732
+ shiftLabel: currentTiming.shiftLabel,
7733
+ shiftStart: formatInTimeZone(currentTiming.shiftStartDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX"),
7734
+ shiftEnd: formatInTimeZone(currentTiming.shiftEndDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX"),
7735
+ totalMinutes: currentTiming.totalMinutes,
7736
+ completedMinutes: currentTiming.completedMinutes,
7737
+ uptimeMinutes: 0,
7738
+ downtimeMinutes: 0,
7739
+ pendingMinutes: currentTiming.pendingMinutes,
7740
+ uptimePercentage: 0,
7741
+ hasData: false,
7742
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
7743
+ points: [],
7744
+ downtimeSegments: []
7745
+ };
7746
+ }
7663
7747
  if (lineId) {
7664
7748
  const backendTimeline = await this.fetchBackendWorkspaceUptimeTimeline(
7665
7749
  workspaceId,
@@ -7882,6 +7966,9 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
7882
7966
  const defaultTimezone = dashboardConfig?.dateTimeConfig?.defaultTimezone || "UTC";
7883
7967
  const shiftConfig = passedShiftConfig || dashboardConfig?.shiftConfig;
7884
7968
  const effectiveTimezone = timezone || shiftConfig?.timezone || defaultTimezone;
7969
+ if (overrideDate !== void 0 && !this.isCanonicalDate(overrideDate)) {
7970
+ return /* @__PURE__ */ new Map();
7971
+ }
7885
7972
  if (lineShiftConfigs && lineShiftConfigs.size > 0) {
7886
7973
  return this.calculateWorkspaceUptimeMultiLine(
7887
7974
  companyId,
@@ -11974,85 +12061,290 @@ function formatReasonLabel(reason) {
11974
12061
  return reason.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
11975
12062
  }
11976
12063
 
12064
+ // src/lib/services/sessionActivityAccumulator.ts
12065
+ var TRACKED_ACTIVITY_EVENTS = ["mousemove", "pointermove", "keydown", "click", "scroll", "wheel", "touchstart"];
12066
+ function createSessionData(sessionId, userId, companyId, now4, isVisible, isFocused) {
12067
+ return {
12068
+ sessionId,
12069
+ userId,
12070
+ companyId,
12071
+ startedAt: now4,
12072
+ lastTick: now4,
12073
+ cumulativeTotalMs: 0,
12074
+ cumulativeActiveMs: 0,
12075
+ cumulativePassiveMs: 0,
12076
+ lastActivityAt: null,
12077
+ isUserActive: false,
12078
+ isVisible,
12079
+ isFocused,
12080
+ state: "ACTIVE",
12081
+ currentSegment: null,
12082
+ pendingSegments: [],
12083
+ segmentCounter: 0
12084
+ };
12085
+ }
12086
+ var SessionActivityAccumulator = class {
12087
+ constructor(idleThresholdMs, getDashboardView) {
12088
+ this.idleThresholdMs = idleThresholdMs;
12089
+ this.getDashboardView = getDashboardView;
12090
+ }
12091
+ shouldTrackTime(session) {
12092
+ if (!session) return false;
12093
+ return session.state === "ACTIVE" && session.isVisible && session.isFocused;
12094
+ }
12095
+ getActivityState(session, now4) {
12096
+ if (!session.lastActivityAt) {
12097
+ return "passive";
12098
+ }
12099
+ const timeSinceActivity = now4.getTime() - session.lastActivityAt.getTime();
12100
+ return timeSinceActivity <= this.idleThresholdMs ? "active" : "passive";
12101
+ }
12102
+ updateTimes(session, now4 = /* @__PURE__ */ new Date()) {
12103
+ if (!session || session.state === "ENDED") return;
12104
+ const elapsed = now4.getTime() - session.lastTick.getTime();
12105
+ if (elapsed <= 0) return;
12106
+ if (!this.shouldTrackTime(session)) {
12107
+ this.flushCurrentSegment(session, now4);
12108
+ session.lastTick = now4;
12109
+ return;
12110
+ }
12111
+ let cursor = new Date(session.lastTick);
12112
+ const idleCutoff = session.lastActivityAt ? new Date(session.lastActivityAt.getTime() + this.idleThresholdMs) : null;
12113
+ if (idleCutoff && cursor < idleCutoff && now4 > idleCutoff) {
12114
+ this.addTrackedDuration(session, "active", cursor, idleCutoff);
12115
+ cursor = idleCutoff;
12116
+ }
12117
+ this.addTrackedDuration(session, this.getActivityState(session, now4), cursor, now4);
12118
+ session.lastTick = now4;
12119
+ }
12120
+ flushCurrentSegment(session, endAt) {
12121
+ if (!session?.currentSegment) return;
12122
+ const current = session.currentSegment;
12123
+ const currentEndMs = new Date(current.segment_end_at).getTime();
12124
+ if (endAt.getTime() > currentEndMs) {
12125
+ current.duration_ms += endAt.getTime() - currentEndMs;
12126
+ current.segment_end_at = endAt.toISOString();
12127
+ }
12128
+ if (current.duration_ms > 0) {
12129
+ session.pendingSegments.push(current);
12130
+ }
12131
+ session.currentSegment = null;
12132
+ }
12133
+ addTrackedDuration(session, state, start, end) {
12134
+ const durationMs = end.getTime() - start.getTime();
12135
+ if (durationMs <= 0) return;
12136
+ session.cumulativeTotalMs += durationMs;
12137
+ if (state === "active") {
12138
+ session.cumulativeActiveMs += durationMs;
12139
+ session.isUserActive = true;
12140
+ } else {
12141
+ session.cumulativePassiveMs += durationMs;
12142
+ session.isUserActive = false;
12143
+ }
12144
+ this.addSegmentDuration(session, state, start, end);
12145
+ }
12146
+ addSegmentDuration(session, state, start, end) {
12147
+ const durationMs = end.getTime() - start.getTime();
12148
+ if (durationMs <= 0) return;
12149
+ const dashboardView = this.getDashboardView();
12150
+ const current = session.currentSegment;
12151
+ if (current && current.state === state && current.dashboard_view === dashboardView && current.segment_end_at === start.toISOString()) {
12152
+ current.segment_end_at = end.toISOString();
12153
+ current.duration_ms += durationMs;
12154
+ return;
12155
+ }
12156
+ this.flushCurrentSegment(session, start);
12157
+ session.segmentCounter += 1;
12158
+ session.currentSegment = {
12159
+ client_segment_id: `${session.sessionId}_seg_${session.segmentCounter}`,
12160
+ segment_start_at: start.toISOString(),
12161
+ segment_end_at: end.toISOString(),
12162
+ state,
12163
+ duration_ms: durationMs,
12164
+ dashboard_view: dashboardView,
12165
+ tracker_version: 2
12166
+ };
12167
+ }
12168
+ };
12169
+
12170
+ // src/lib/services/sessionTrackerTransport.ts
12171
+ var SessionTrackerTransport = class {
12172
+ constructor(config, trackerVersion, maxSegmentsPerRequest) {
12173
+ this.config = config;
12174
+ this.trackerVersion = trackerVersion;
12175
+ this.maxSegmentsPerRequest = maxSegmentsPerRequest;
12176
+ }
12177
+ async sendStartSession(session) {
12178
+ const token = await this.config.getAccessToken();
12179
+ if (!token) {
12180
+ console.warn("[SessionTracker] No access token, skipping start session API call");
12181
+ return;
12182
+ }
12183
+ await fetch(`${this.config.apiBaseUrl}/api/sessions/start`, {
12184
+ method: "POST",
12185
+ headers: this.authHeaders(token),
12186
+ body: JSON.stringify({
12187
+ session_id: session.sessionId,
12188
+ user_agent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
12189
+ dashboard_view: this.getDashboardView(),
12190
+ tracker_version: this.trackerVersion
12191
+ })
12192
+ });
12193
+ }
12194
+ async sendHeartbeat(session) {
12195
+ const token = await this.config.getAccessToken();
12196
+ if (!token) return null;
12197
+ const segmentsToSend = session.pendingSegments.slice(0, this.maxSegmentsPerRequest);
12198
+ const sentSegmentIds = new Set(segmentsToSend.map((segment) => segment.client_segment_id));
12199
+ const response = await fetch(`${this.config.apiBaseUrl}/api/sessions/heartbeat`, {
12200
+ method: "POST",
12201
+ headers: this.authHeaders(token),
12202
+ body: JSON.stringify({
12203
+ session_id: session.sessionId,
12204
+ cumulative_total_ms: session.cumulativeTotalMs,
12205
+ cumulative_active_ms: session.cumulativeActiveMs,
12206
+ cumulative_passive_ms: session.cumulativePassiveMs,
12207
+ visibility_state: typeof document !== "undefined" ? document.visibilityState : "visible",
12208
+ is_user_active: session.isUserActive,
12209
+ dashboard_view: this.getDashboardView(),
12210
+ tracker_version: this.trackerVersion,
12211
+ activity_segments: segmentsToSend
12212
+ })
12213
+ });
12214
+ const payload = await response.json().catch(() => ({}));
12215
+ return {
12216
+ ok: response.ok,
12217
+ acknowledged: payload?.acknowledged === false ? false : payload?.acknowledged === true ? true : null,
12218
+ sentSegmentIds
12219
+ };
12220
+ }
12221
+ async sendEndSession(session, reason, keepalive) {
12222
+ const token = keepalive && this.config.getCachedAccessToken ? this.config.getCachedAccessToken() : await this.config.getAccessToken();
12223
+ if (!token) {
12224
+ console.warn("[SessionTracker] No access token, skipping end session API call");
12225
+ return false;
12226
+ }
12227
+ const response = await fetch(`${this.config.apiBaseUrl}/api/sessions/end`, {
12228
+ method: "POST",
12229
+ keepalive,
12230
+ headers: this.authHeaders(token),
12231
+ body: JSON.stringify({
12232
+ session_id: session.sessionId,
12233
+ end_reason: reason,
12234
+ final_total_ms: session.cumulativeTotalMs,
12235
+ final_active_ms: session.cumulativeActiveMs,
12236
+ final_passive_ms: session.cumulativePassiveMs,
12237
+ tracker_version: this.trackerVersion,
12238
+ activity_segments: session.pendingSegments.slice(0, this.maxSegmentsPerRequest)
12239
+ })
12240
+ });
12241
+ return response.ok;
12242
+ }
12243
+ authHeaders(token) {
12244
+ return {
12245
+ "Content-Type": "application/json",
12246
+ "Authorization": `Bearer ${token}`
12247
+ };
12248
+ }
12249
+ getDashboardView() {
12250
+ return typeof window !== "undefined" ? window.location.pathname : void 0;
12251
+ }
12252
+ };
12253
+
11977
12254
  // src/lib/services/sessionTracker.ts
11978
12255
  function generateSessionId() {
11979
12256
  return `sess_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
11980
12257
  }
12258
+ function isDebugEnabled() {
12259
+ return typeof process !== "undefined" && process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
12260
+ }
12261
+ function debugLog(message, data) {
12262
+ if (isDebugEnabled()) {
12263
+ console.log(`[SessionTracker] ${message}`, data || {});
12264
+ }
12265
+ }
11981
12266
  var SessionTracker = class {
11982
12267
  constructor(config) {
11983
12268
  this.session = null;
11984
12269
  this.heartbeatInterval = null;
11985
- this.graceTimeout = null;
11986
12270
  this.tickInterval = null;
11987
- // Constants
12271
+ this.heartbeatInFlight = false;
12272
+ this.pendingForcedHeartbeat = false;
12273
+ this.TRACKER_VERSION = 2;
11988
12274
  this.HEARTBEAT_INTERVAL_MS = 3e4;
11989
- // 30 seconds
11990
- this.HIDDEN_GRACE_PERIOD_MS = 3e5;
11991
- // 5 minutes
11992
12275
  this.IDLE_THRESHOLD_MS = 3e4;
11993
- // 30 seconds without activity = passive
11994
12276
  this.TICK_INTERVAL_MS = 1e3;
12277
+ this.MAX_SEGMENTS_PER_REQUEST = 200;
11995
12278
  this.config = config;
12279
+ this.activityAccumulator = new SessionActivityAccumulator(
12280
+ this.IDLE_THRESHOLD_MS,
12281
+ () => this.getDashboardView()
12282
+ );
12283
+ this.transport = new SessionTrackerTransport(
12284
+ this.config,
12285
+ this.TRACKER_VERSION,
12286
+ this.MAX_SEGMENTS_PER_REQUEST
12287
+ );
11996
12288
  this.boundHandleActivity = this.handleUserActivity.bind(this);
11997
12289
  this.boundHandleVisibility = this.handleVisibilityChange.bind(this);
11998
12290
  this.boundHandleBeforeUnload = this.handleBeforeUnload.bind(this);
12291
+ this.boundHandlePageHide = this.handlePageHide.bind(this);
12292
+ this.boundHandlePageShow = this.handlePageShow.bind(this);
11999
12293
  this.boundHandleFocus = this.handleWindowFocus.bind(this);
12000
12294
  this.boundHandleBlur = this.handleWindowBlur.bind(this);
12001
12295
  }
12002
- /**
12003
- * Start a new session
12004
- */
12005
12296
  async startSession(userId, companyId) {
12006
12297
  if (this.session && this.session.state !== "ENDED") {
12007
- console.log("[SessionTracker] Session already active, ignoring start");
12298
+ debugLog("Session already active, ignoring start", { sessionId: this.session.sessionId });
12008
12299
  return;
12009
12300
  }
12010
12301
  const sessionId = generateSessionId();
12011
12302
  const now4 = /* @__PURE__ */ new Date();
12012
- this.session = {
12303
+ this.session = createSessionData(
12013
12304
  sessionId,
12014
12305
  userId,
12015
12306
  companyId,
12016
- startedAt: now4,
12017
- lastTick: now4,
12018
- cumulativeTotalMs: 0,
12019
- cumulativeActiveMs: 0,
12020
- cumulativePassiveMs: 0,
12021
- lastActivityAt: now4,
12022
- isUserActive: true,
12023
- isVisible: typeof document !== "undefined" ? document.visibilityState === "visible" : true,
12024
- isFocused: typeof document !== "undefined" && typeof document.hasFocus === "function" ? document.hasFocus() : true,
12025
- state: "ACTIVE",
12026
- hiddenSince: null
12027
- };
12307
+ now4,
12308
+ this.getDocumentVisibility(),
12309
+ this.getWindowFocus()
12310
+ );
12311
+ await this.sendStartSession();
12312
+ if (!this.session || this.session.sessionId !== sessionId) {
12313
+ return;
12314
+ }
12315
+ if (this.session.state === "ENDED") {
12316
+ await this.sendEndSession(this.session.endReason || "navigation");
12317
+ debugLog("Session start completed after local end", { sessionId });
12318
+ return;
12319
+ }
12320
+ const trackingStart = /* @__PURE__ */ new Date();
12321
+ this.session.lastTick = trackingStart;
12322
+ this.session.isVisible = this.getDocumentVisibility();
12323
+ this.session.isFocused = this.getWindowFocus();
12028
12324
  this.setupActivityListeners();
12029
12325
  this.setupVisibilityListener();
12030
12326
  this.setupFocusListeners();
12031
- this.setupBeforeUnloadListener();
12327
+ this.setupPageLifecycleListeners();
12032
12328
  this.startTimeTick();
12033
12329
  this.startHeartbeat();
12034
- await this.sendStartSession();
12035
12330
  this.config.onSessionStart?.(sessionId);
12036
- console.log("[SessionTracker] Session started:", sessionId);
12331
+ debugLog("Session started", { sessionId });
12037
12332
  }
12038
- /**
12039
- * End the current session
12040
- */
12041
12333
  async endSession(reason) {
12042
12334
  if (!this.session || this.session.state === "ENDED") {
12043
12335
  return;
12044
12336
  }
12045
12337
  const sessionId = this.session.sessionId;
12046
12338
  this.updateTimes();
12339
+ this.flushCurrentSegment(/* @__PURE__ */ new Date());
12340
+ this.session.endReason = reason;
12341
+ await this.drainPendingSegmentsBeforeEnd();
12047
12342
  this.session.state = "ENDED";
12048
12343
  this.cleanup();
12049
12344
  await this.sendEndSession(reason);
12050
12345
  this.config.onSessionEnd?.(sessionId, reason);
12051
- console.log("[SessionTracker] Session ended:", sessionId, "reason:", reason);
12346
+ debugLog("Session ended", { sessionId, reason });
12052
12347
  }
12053
- /**
12054
- * Get current session stats (for debugging/display)
12055
- */
12056
12348
  getSessionStats() {
12057
12349
  if (!this.session) return null;
12058
12350
  return {
@@ -12061,11 +12353,9 @@ var SessionTracker = class {
12061
12353
  passiveMs: this.session.cumulativePassiveMs
12062
12354
  };
12063
12355
  }
12064
- // ===== Private Methods =====
12065
12356
  setupActivityListeners() {
12066
12357
  if (typeof window === "undefined") return;
12067
- const events = ["mousemove", "keydown", "click", "scroll", "touchstart"];
12068
- events.forEach((event) => {
12358
+ TRACKED_ACTIVITY_EVENTS.forEach((event) => {
12069
12359
  window.addEventListener(event, this.boundHandleActivity, { passive: true });
12070
12360
  });
12071
12361
  }
@@ -12078,71 +12368,100 @@ var SessionTracker = class {
12078
12368
  window.addEventListener("focus", this.boundHandleFocus);
12079
12369
  window.addEventListener("blur", this.boundHandleBlur);
12080
12370
  }
12081
- setupBeforeUnloadListener() {
12371
+ setupPageLifecycleListeners() {
12082
12372
  if (typeof window === "undefined") return;
12083
12373
  window.addEventListener("beforeunload", this.boundHandleBeforeUnload);
12374
+ window.addEventListener("pagehide", this.boundHandlePageHide);
12375
+ window.addEventListener("pageshow", this.boundHandlePageShow);
12084
12376
  }
12085
- handleUserActivity() {
12377
+ handleUserActivity(event) {
12086
12378
  if (!this.session || this.session.state === "ENDED") return;
12379
+ if ("isTrusted" in event && event.isTrusted === false) return;
12380
+ this.updateTimes();
12087
12381
  this.session.lastActivityAt = /* @__PURE__ */ new Date();
12088
12382
  this.session.isUserActive = true;
12089
12383
  }
12090
12384
  handleVisibilityChange() {
12091
12385
  if (!this.session || this.session.state === "ENDED") return;
12386
+ const now4 = /* @__PURE__ */ new Date();
12387
+ this.updateTimes(now4);
12092
12388
  if (document.visibilityState === "hidden") {
12093
- this.session.hiddenSince = /* @__PURE__ */ new Date();
12094
- this.session.state = "BACKGROUND_GRACE";
12389
+ this.flushCurrentSegment(now4);
12095
12390
  this.session.isVisible = false;
12096
12391
  this.session.isFocused = false;
12097
- this.session.lastTick = /* @__PURE__ */ new Date();
12098
- this.graceTimeout = setTimeout(() => {
12099
- this.endSession("hidden_timeout");
12100
- }, this.HIDDEN_GRACE_PERIOD_MS);
12101
- console.log("[SessionTracker] Tab hidden, starting grace period");
12102
- } else {
12103
- if (this.graceTimeout) {
12104
- clearTimeout(this.graceTimeout);
12105
- this.graceTimeout = null;
12106
- }
12107
- this.session.hiddenSince = null;
12108
- this.session.state = "ACTIVE";
12109
- this.session.isVisible = true;
12110
- this.session.isFocused = typeof document !== "undefined" && typeof document.hasFocus === "function" ? document.hasFocus() : true;
12111
- this.session.lastActivityAt = /* @__PURE__ */ new Date();
12112
- this.session.isUserActive = true;
12113
- this.session.lastTick = /* @__PURE__ */ new Date();
12114
- console.log("[SessionTracker] Tab visible again");
12392
+ this.session.isUserActive = false;
12393
+ this.session.lastActivityAt = null;
12394
+ this.session.lastTick = now4;
12395
+ void this.sendHeartbeat(true);
12396
+ debugLog("Tab hidden");
12397
+ return;
12115
12398
  }
12399
+ this.session.isVisible = true;
12400
+ this.session.isFocused = this.getWindowFocus();
12401
+ this.session.isUserActive = false;
12402
+ this.session.lastActivityAt = null;
12403
+ this.session.lastTick = now4;
12404
+ void this.sendHeartbeat(true);
12405
+ debugLog("Tab visible");
12116
12406
  }
12117
12407
  handleWindowFocus() {
12118
12408
  if (!this.session || this.session.state === "ENDED") return;
12409
+ const now4 = /* @__PURE__ */ new Date();
12410
+ this.updateTimes(now4);
12119
12411
  this.session.isFocused = true;
12120
- this.session.lastActivityAt = /* @__PURE__ */ new Date();
12121
- this.session.isUserActive = true;
12122
- this.session.lastTick = /* @__PURE__ */ new Date();
12412
+ this.session.isUserActive = false;
12413
+ this.session.lastActivityAt = null;
12414
+ this.session.lastTick = now4;
12415
+ void this.sendHeartbeat(true);
12123
12416
  }
12124
12417
  handleWindowBlur() {
12125
12418
  if (!this.session || this.session.state === "ENDED") return;
12419
+ const now4 = /* @__PURE__ */ new Date();
12420
+ this.updateTimes(now4);
12421
+ this.flushCurrentSegment(now4);
12126
12422
  this.session.isFocused = false;
12127
- this.session.lastTick = /* @__PURE__ */ new Date();
12423
+ this.session.isUserActive = false;
12424
+ this.session.lastActivityAt = null;
12425
+ this.session.lastTick = now4;
12426
+ void this.sendHeartbeat(true);
12128
12427
  }
12129
- handleBeforeUnload(_e) {
12130
- if (this.session && this.session.state !== "ENDED") {
12131
- this.updateTimes();
12132
- const data = {
12133
- session_id: this.session.sessionId,
12134
- end_reason: "tab_close",
12135
- final_total_ms: this.session.cumulativeTotalMs,
12136
- final_active_ms: this.session.cumulativeActiveMs,
12137
- final_passive_ms: this.session.cumulativePassiveMs
12138
- };
12139
- if (navigator.sendBeacon) {
12140
- const blob = new Blob([JSON.stringify(data)], { type: "application/json" });
12141
- navigator.sendBeacon(`${this.config.apiBaseUrl}/api/sessions/end`, blob);
12142
- }
12143
- this.session.state = "ENDED";
12144
- this.cleanup();
12428
+ handleBeforeUnload() {
12429
+ this.finalizeWithKeepalive("tab_close");
12430
+ }
12431
+ handlePageHide(event) {
12432
+ if (event.persisted) {
12433
+ if (!this.session || this.session.state === "ENDED") return;
12434
+ const now4 = /* @__PURE__ */ new Date();
12435
+ this.updateTimes(now4);
12436
+ this.flushCurrentSegment(now4);
12437
+ this.session.isVisible = false;
12438
+ this.session.isFocused = false;
12439
+ this.session.isUserActive = false;
12440
+ this.session.lastActivityAt = null;
12441
+ this.session.lastTick = now4;
12442
+ void this.sendHeartbeat(true);
12443
+ return;
12145
12444
  }
12445
+ this.finalizeWithKeepalive("pagehide");
12446
+ }
12447
+ handlePageShow(event) {
12448
+ if (!event.persisted || !this.session || this.session.state === "ENDED") return;
12449
+ const now4 = /* @__PURE__ */ new Date();
12450
+ this.session.isVisible = this.getDocumentVisibility();
12451
+ this.session.isFocused = this.getWindowFocus();
12452
+ this.session.isUserActive = false;
12453
+ this.session.lastActivityAt = null;
12454
+ this.session.lastTick = now4;
12455
+ void this.sendHeartbeat(true);
12456
+ }
12457
+ finalizeWithKeepalive(reason) {
12458
+ if (!this.session || this.session.state === "ENDED") return;
12459
+ this.updateTimes();
12460
+ this.flushCurrentSegment(/* @__PURE__ */ new Date());
12461
+ this.session.endReason = reason;
12462
+ this.session.state = "ENDED";
12463
+ void this.sendEndSession(reason, true);
12464
+ this.cleanup();
12146
12465
  }
12147
12466
  startTimeTick() {
12148
12467
  this.tickInterval = setInterval(() => {
@@ -12150,30 +12469,17 @@ var SessionTracker = class {
12150
12469
  }, this.TICK_INTERVAL_MS);
12151
12470
  }
12152
12471
  shouldTrackTime() {
12153
- if (!this.session) return false;
12154
- const stateAllowsTracking = this.session.state === "ACTIVE" || this.session.state === "BACKGROUND_GRACE";
12155
- return stateAllowsTracking && this.session.isVisible && this.session.isFocused;
12472
+ return this.activityAccumulator.shouldTrackTime(this.session);
12156
12473
  }
12157
- updateTimes() {
12158
- if (!this.session || this.session.state === "ENDED") return;
12159
- const now4 = /* @__PURE__ */ new Date();
12160
- const elapsed = now4.getTime() - this.session.lastTick.getTime();
12161
- if (!this.shouldTrackTime()) {
12162
- this.session.lastTick = now4;
12163
- return;
12164
- }
12165
- if (this.session.state === "ACTIVE" || this.session.state === "BACKGROUND_GRACE") {
12166
- this.session.cumulativeTotalMs += elapsed;
12167
- const timeSinceActivity = now4.getTime() - this.session.lastActivityAt.getTime();
12168
- if (timeSinceActivity < this.IDLE_THRESHOLD_MS) {
12169
- this.session.cumulativeActiveMs += elapsed;
12170
- this.session.isUserActive = true;
12171
- } else {
12172
- this.session.cumulativePassiveMs += elapsed;
12173
- this.session.isUserActive = false;
12174
- }
12175
- }
12176
- this.session.lastTick = now4;
12474
+ getActivityState(now4) {
12475
+ if (!this.session) return "passive";
12476
+ return this.activityAccumulator.getActivityState(this.session, now4);
12477
+ }
12478
+ updateTimes(now4 = /* @__PURE__ */ new Date()) {
12479
+ this.activityAccumulator.updateTimes(this.session, now4);
12480
+ }
12481
+ flushCurrentSegment(endAt) {
12482
+ this.activityAccumulator.flushCurrentSegment(this.session, endAt);
12177
12483
  }
12178
12484
  startHeartbeat() {
12179
12485
  this.heartbeatInterval = setInterval(async () => {
@@ -12183,75 +12489,70 @@ var SessionTracker = class {
12183
12489
  async sendStartSession() {
12184
12490
  if (!this.session) return;
12185
12491
  try {
12186
- const token = await this.config.getAccessToken();
12187
- if (!token) {
12188
- console.warn("[SessionTracker] No access token, skipping start session API call");
12189
- return;
12190
- }
12191
- await fetch(`${this.config.apiBaseUrl}/api/sessions/start`, {
12192
- method: "POST",
12193
- headers: {
12194
- "Content-Type": "application/json",
12195
- "Authorization": `Bearer ${token}`
12196
- },
12197
- body: JSON.stringify({
12198
- session_id: this.session.sessionId,
12199
- user_agent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
12200
- dashboard_view: typeof window !== "undefined" ? window.location.pathname : void 0
12201
- })
12202
- });
12492
+ await this.transport.sendStartSession(this.session);
12203
12493
  } catch (error) {
12204
12494
  console.error("[SessionTracker] Failed to send start session:", error);
12205
12495
  }
12206
12496
  }
12207
- async sendHeartbeat() {
12497
+ async sendHeartbeat(force = false, allowReplacement = true) {
12498
+ if (this.heartbeatInFlight) {
12499
+ this.pendingForcedHeartbeat || (this.pendingForcedHeartbeat = force);
12500
+ return;
12501
+ }
12208
12502
  if (!this.session || this.session.state === "ENDED") return;
12503
+ this.heartbeatInFlight = true;
12209
12504
  try {
12210
- const token = await this.config.getAccessToken();
12211
- if (!token) return;
12212
- await fetch(`${this.config.apiBaseUrl}/api/sessions/heartbeat`, {
12213
- method: "POST",
12214
- headers: {
12215
- "Content-Type": "application/json",
12216
- "Authorization": `Bearer ${token}`
12217
- },
12218
- body: JSON.stringify({
12219
- session_id: this.session.sessionId,
12220
- cumulative_total_ms: this.session.cumulativeTotalMs,
12221
- cumulative_active_ms: this.session.cumulativeActiveMs,
12222
- cumulative_passive_ms: this.session.cumulativePassiveMs,
12223
- visibility_state: typeof document !== "undefined" ? document.visibilityState : "visible",
12224
- is_user_active: this.session.isUserActive,
12225
- dashboard_view: typeof window !== "undefined" ? window.location.pathname : void 0
12226
- })
12227
- });
12505
+ this.updateTimes();
12506
+ this.flushCurrentSegment(/* @__PURE__ */ new Date());
12507
+ if (!force && !this.shouldTrackTime()) return;
12508
+ const result = await this.transport.sendHeartbeat(this.session);
12509
+ if (!result) return;
12510
+ if (result.ok && result.acknowledged !== false) {
12511
+ debugLog("Heartbeat acknowledged", { segments: result.sentSegmentIds.size });
12512
+ this.session.pendingSegments = this.session.pendingSegments.filter(
12513
+ (segment) => !result.sentSegmentIds.has(segment.client_segment_id)
12514
+ );
12515
+ } else if (result.ok && result.acknowledged === false && allowReplacement && this.shouldTrackTime()) {
12516
+ const { userId, companyId, sessionId } = this.session;
12517
+ this.session.state = "ENDED";
12518
+ this.cleanup();
12519
+ debugLog("Heartbeat not acknowledged, starting replacement session", { sessionId });
12520
+ await this.startSession(userId, companyId);
12521
+ }
12228
12522
  } catch (error) {
12229
12523
  console.error("[SessionTracker] Failed to send heartbeat:", error);
12524
+ } finally {
12525
+ this.heartbeatInFlight = false;
12526
+ if (this.pendingForcedHeartbeat && this.session?.state !== "ENDED") {
12527
+ this.pendingForcedHeartbeat = false;
12528
+ void this.sendHeartbeat(true);
12529
+ }
12530
+ }
12531
+ }
12532
+ async drainPendingSegmentsBeforeEnd() {
12533
+ if (!this.session) return;
12534
+ let guard = 0;
12535
+ while (this.session.state !== "ENDED" && this.session.pendingSegments.length > this.MAX_SEGMENTS_PER_REQUEST && guard < 20) {
12536
+ const pendingBefore = this.session.pendingSegments.length;
12537
+ await this.sendHeartbeat(true, false);
12538
+ if (!this.session || this.session.pendingSegments.length >= pendingBefore) {
12539
+ break;
12540
+ }
12541
+ guard += 1;
12230
12542
  }
12231
12543
  }
12232
- async sendEndSession(reason) {
12544
+ async sendEndSession(reason, keepalive = false) {
12233
12545
  if (!this.session) return;
12234
12546
  try {
12235
- const token = await this.config.getAccessToken();
12236
- if (!token) {
12237
- console.warn("[SessionTracker] No access token, skipping end session API call");
12238
- return;
12547
+ const ok = await this.transport.sendEndSession(this.session, reason, keepalive);
12548
+ if (ok) {
12549
+ this.session.pendingSegments = this.session.pendingSegments.slice(this.MAX_SEGMENTS_PER_REQUEST);
12239
12550
  }
12240
- await fetch(`${this.config.apiBaseUrl}/api/sessions/end`, {
12241
- method: "POST",
12242
- headers: {
12243
- "Content-Type": "application/json",
12244
- "Authorization": `Bearer ${token}`
12245
- },
12246
- body: JSON.stringify({
12247
- session_id: this.session.sessionId,
12248
- end_reason: reason,
12249
- final_total_ms: this.session.cumulativeTotalMs,
12250
- final_active_ms: this.session.cumulativeActiveMs,
12251
- final_passive_ms: this.session.cumulativePassiveMs
12252
- })
12253
- });
12254
12551
  } catch (error) {
12552
+ if (keepalive) {
12553
+ debugLog("Keepalive end session failed after page lifecycle transition", { reason });
12554
+ return;
12555
+ }
12255
12556
  console.error("[SessionTracker] Failed to send end session:", error);
12256
12557
  }
12257
12558
  }
@@ -12264,23 +12565,29 @@ var SessionTracker = class {
12264
12565
  clearInterval(this.tickInterval);
12265
12566
  this.tickInterval = null;
12266
12567
  }
12267
- if (this.graceTimeout) {
12268
- clearTimeout(this.graceTimeout);
12269
- this.graceTimeout = null;
12270
- }
12271
12568
  if (typeof window !== "undefined") {
12272
- const events = ["mousemove", "keydown", "click", "scroll", "touchstart"];
12273
- events.forEach((event) => {
12569
+ TRACKED_ACTIVITY_EVENTS.forEach((event) => {
12274
12570
  window.removeEventListener(event, this.boundHandleActivity);
12275
12571
  });
12276
12572
  window.removeEventListener("focus", this.boundHandleFocus);
12277
12573
  window.removeEventListener("blur", this.boundHandleBlur);
12278
12574
  window.removeEventListener("beforeunload", this.boundHandleBeforeUnload);
12575
+ window.removeEventListener("pagehide", this.boundHandlePageHide);
12576
+ window.removeEventListener("pageshow", this.boundHandlePageShow);
12279
12577
  }
12280
12578
  if (typeof document !== "undefined") {
12281
12579
  document.removeEventListener("visibilitychange", this.boundHandleVisibility);
12282
12580
  }
12283
12581
  }
12582
+ getDashboardView() {
12583
+ return typeof window !== "undefined" ? window.location.pathname : void 0;
12584
+ }
12585
+ getDocumentVisibility() {
12586
+ return typeof document !== "undefined" ? document.visibilityState === "visible" : true;
12587
+ }
12588
+ getWindowFocus() {
12589
+ return typeof document !== "undefined" && typeof document.hasFocus === "function" ? document.hasFocus() : true;
12590
+ }
12284
12591
  };
12285
12592
  function createSessionTracker(config) {
12286
12593
  return new SessionTracker(config);
@@ -13286,15 +13593,43 @@ function useSessionTracking(options = {}) {
13286
13593
  const trackerRef = useRef(null);
13287
13594
  const isTrackingRef = useRef(false);
13288
13595
  const initRef = useRef(false);
13289
- const getAccessToken2 = useCallback(async () => {
13290
- return session?.access_token || null;
13596
+ const accessTokenRef = useRef(null);
13597
+ const isAuthenticatedRef = useRef(isAuthenticated);
13598
+ const onSessionStartRef = useRef(onSessionStart);
13599
+ const onSessionEndRef = useRef(onSessionEnd);
13600
+ const apiBaseUrl2 = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
13601
+ isAuthenticatedRef.current = isAuthenticated;
13602
+ useEffect(() => {
13603
+ if (session?.access_token) {
13604
+ accessTokenRef.current = session.access_token;
13605
+ }
13291
13606
  }, [session?.access_token]);
13607
+ useEffect(() => {
13608
+ onSessionStartRef.current = onSessionStart;
13609
+ }, [onSessionStart]);
13610
+ useEffect(() => {
13611
+ onSessionEndRef.current = onSessionEnd;
13612
+ }, [onSessionEnd]);
13613
+ const getAccessToken2 = useCallback(async () => {
13614
+ return accessTokenRef.current;
13615
+ }, []);
13616
+ const endCurrentTracker = useCallback((reason) => {
13617
+ const tracker = trackerRef.current;
13618
+ if (!tracker) return null;
13619
+ const tokenForEndingSession = accessTokenRef.current;
13620
+ trackerRef.current = null;
13621
+ isTrackingRef.current = false;
13622
+ return tracker.endSession(reason).finally(() => {
13623
+ if (reason === "logout" && !isAuthenticatedRef.current && accessTokenRef.current === tokenForEndingSession) {
13624
+ accessTokenRef.current = null;
13625
+ }
13626
+ });
13627
+ }, []);
13292
13628
  useEffect(() => {
13293
13629
  if (!enabled || !isAuthenticated || !user || !session || initRef.current) {
13294
13630
  return;
13295
13631
  }
13296
13632
  initRef.current = true;
13297
- const apiBaseUrl2 = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
13298
13633
  if (!apiBaseUrl2) {
13299
13634
  console.warn("[useSessionTracking] No API base URL configured, session tracking disabled");
13300
13635
  return;
@@ -13302,41 +13637,34 @@ function useSessionTracking(options = {}) {
13302
13637
  const tracker = createSessionTracker({
13303
13638
  apiBaseUrl: apiBaseUrl2,
13304
13639
  getAccessToken: getAccessToken2,
13640
+ getCachedAccessToken: () => accessTokenRef.current,
13305
13641
  onSessionStart: (sessionId) => {
13306
13642
  isTrackingRef.current = true;
13307
- onSessionStart?.(sessionId);
13643
+ onSessionStartRef.current?.(sessionId);
13308
13644
  },
13309
13645
  onSessionEnd: (sessionId, reason) => {
13310
13646
  isTrackingRef.current = false;
13311
- onSessionEnd?.(sessionId, reason);
13647
+ onSessionEndRef.current?.(sessionId, reason);
13312
13648
  }
13313
13649
  });
13314
13650
  trackerRef.current = tracker;
13315
13651
  const companyId = user?.company_id || user?.properties?.company_id || null;
13316
13652
  tracker.startSession(user.id, companyId);
13317
13653
  return () => {
13318
- if (trackerRef.current) {
13319
- trackerRef.current.endSession("navigation");
13320
- trackerRef.current = null;
13321
- }
13654
+ const reason = isAuthenticatedRef.current ? "navigation" : "logout";
13655
+ void endCurrentTracker(reason);
13322
13656
  initRef.current = false;
13323
13657
  isTrackingRef.current = false;
13324
13658
  };
13325
- }, [enabled, isAuthenticated, user?.id, session?.access_token, config?.apiBaseUrl, getAccessToken2, onSessionStart, onSessionEnd, user]);
13659
+ }, [enabled, isAuthenticated, user?.id, apiBaseUrl2, getAccessToken2, endCurrentTracker]);
13326
13660
  useEffect(() => {
13327
13661
  if (!isAuthenticated && trackerRef.current && isTrackingRef.current) {
13328
- trackerRef.current.endSession("logout");
13329
- trackerRef.current = null;
13330
- isTrackingRef.current = false;
13662
+ void endCurrentTracker("logout");
13331
13663
  }
13332
- }, [isAuthenticated]);
13664
+ }, [isAuthenticated, endCurrentTracker]);
13333
13665
  const endSession = useCallback(async (reason) => {
13334
- if (trackerRef.current) {
13335
- await trackerRef.current.endSession(reason);
13336
- trackerRef.current = null;
13337
- isTrackingRef.current = false;
13338
- }
13339
- }, []);
13666
+ await endCurrentTracker(reason);
13667
+ }, [endCurrentTracker]);
13340
13668
  const sessionStats = trackerRef.current?.getSessionStats() || null;
13341
13669
  return {
13342
13670
  sessionStats,
@@ -15648,6 +15976,10 @@ var useDashboardMetrics = ({
15648
15976
  }
15649
15977
  const isFactory = currentLineIdToUse === factoryViewId;
15650
15978
  const targetLineIds = isFactory ? targetFactoryLineIds : [currentLineIdToUse];
15979
+ if (isFactory && targetLineIds.length > 0 && !shiftGroupsKey) {
15980
+ logDebug("[useDashboardMetrics] Skipping fetch: factory shift groups are not ready");
15981
+ return;
15982
+ }
15651
15983
  const targetLineIdsKey = targetLineIds.slice().sort().join(",");
15652
15984
  const effectiveBlueComparisonLineIds = normalizedBlueComparisonLineIds.length ? normalizedBlueComparisonLineIds : targetLineIds;
15653
15985
  const blueComparisonLineIdsKey = effectiveBlueComparisonLineIds.slice().sort().join(",");
@@ -15712,7 +16044,7 @@ var useDashboardMetrics = ({
15712
16044
  const qaGreenStreakParams = qaGreenStreakParamString ? `&${qaGreenStreakParamString}` : "";
15713
16045
  const buildMetricsEndpoint = (params) => {
15714
16046
  const lineIdsParam = isFactory ? `line_ids=${params.groupLineIds.join(",")}` : `line_id=${params.groupLineIds[0]}`;
15715
- const blueComparisonParam = effectiveBlueComparisonLineIds.length ? `&blue_comparison_line_ids=${effectiveBlueComparisonLineIds.join(",")}` : "";
16047
+ const blueComparisonParam = normalizedBlueComparisonLineIds.length ? `&blue_comparison_line_ids=${normalizedBlueComparisonLineIds.join(",")}` : "";
15716
16048
  const selectorParam = normalizedSelectorLineIds.length ? `&selector_line_ids=${encodeURIComponent(normalizedSelectorLineIds.join(","))}` : "";
15717
16049
  return `/api/dashboard/metrics?${lineIdsParam}${blueComparisonParam}${selectorParam}&date=${params.date}&shift_id=${params.shiftId}&company_id=${companyId}${forceParam}${qaGreenStreakParams}`;
15718
16050
  };
@@ -17435,7 +17767,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
17435
17767
  return Number.isFinite(parsed) ? parsed : DEFAULT_MAX_MANIFEST_AGE_MS;
17436
17768
  })();
17437
17769
  const manifestStaleThresholdMs = maxManifestAgeMs > 0 ? Math.min(maxManifestAgeMs, SEGMENT_MAX_AGE_MS) : SEGMENT_MAX_AGE_MS;
17438
- const debugLog = (...args) => {
17770
+ const debugLog2 = (...args) => {
17439
17771
  if (debugEnabled) {
17440
17772
  console.log(...args);
17441
17773
  }
@@ -17617,7 +17949,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
17617
17949
  return;
17618
17950
  }
17619
17951
  } catch (error) {
17620
- debugLog("[HLS] Stale manifest poll failed", error);
17952
+ debugLog2("[HLS] Stale manifest poll failed", error);
17621
17953
  }
17622
17954
  staleManifestPollDelayRef.current = Math.min(
17623
17955
  staleManifestPollDelayRef.current + 5e3,
@@ -17967,7 +18299,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
17967
18299
  }
17968
18300
  return true;
17969
18301
  } catch (error) {
17970
- debugLog("[HLS] Manifest freshness check failed", error);
18302
+ debugLog2("[HLS] Manifest freshness check failed", error);
17971
18303
  {
17972
18304
  markStaleStream("manifest freshness check failed");
17973
18305
  return false;
@@ -18013,7 +18345,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18013
18345
  };
18014
18346
  const softRestart = (reason) => {
18015
18347
  if (staleManifestTriggeredRef.current) {
18016
- debugLog("[HLS] Skip soft restart while manifest is stale", reason);
18348
+ debugLog2("[HLS] Skip soft restart while manifest is stale", reason);
18017
18349
  return;
18018
18350
  }
18019
18351
  console.warn(`[HLS] Soft restart: ${reason}`);
@@ -18046,7 +18378,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18046
18378
  };
18047
18379
  const hardRestart = (reason) => {
18048
18380
  if (staleManifestTriggeredRef.current) {
18049
- debugLog("[HLS] Skip hard restart while manifest is stale", reason);
18381
+ debugLog2("[HLS] Skip hard restart while manifest is stale", reason);
18050
18382
  return;
18051
18383
  }
18052
18384
  console.warn(`[HLS] Hard restart: ${reason}`);
@@ -18300,7 +18632,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18300
18632
  hls.loadSource(resolvedHlsSrc);
18301
18633
  activeStreamUrlRef.current = resolvedHlsSrc;
18302
18634
  hls.on(Hls.Events.ERROR, (_, data) => {
18303
- debugLog("[HLS] Error", {
18635
+ debugLog2("[HLS] Error", {
18304
18636
  type: data.type,
18305
18637
  details: data.details,
18306
18638
  fatal: data.fatal,
@@ -18308,7 +18640,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18308
18640
  frag: data.frag?.sn
18309
18641
  });
18310
18642
  if (data.type === Hls.ErrorTypes.MEDIA_ERROR && data.details === Hls.ErrorDetails.BUFFER_STALLED_ERROR) {
18311
- debugLog("[HLS] Buffer stalled, waiting for next segment");
18643
+ debugLog2("[HLS] Buffer stalled, waiting for next segment");
18312
18644
  attemptPlay();
18313
18645
  return;
18314
18646
  }
@@ -18452,7 +18784,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18452
18784
  }
18453
18785
  }
18454
18786
  }
18455
- debugLog("[HLS] Level loaded", {
18787
+ debugLog2("[HLS] Level loaded", {
18456
18788
  targetduration: details.targetduration,
18457
18789
  edge: details.edge,
18458
18790
  fragments: data.details?.fragments?.length
@@ -18848,6 +19180,7 @@ var isInitializing = false;
18848
19180
  var initializedWithLineIds = [];
18849
19181
  var missingLineContextWarnings = /* @__PURE__ */ new Set();
18850
19182
  var lineLoadPromises = /* @__PURE__ */ new Map();
19183
+ var GLOBAL_CACHE_KEY = "global";
18851
19184
  var initializationPromise = null;
18852
19185
  var workspaceDisplayNamesListeners = /* @__PURE__ */ new Set();
18853
19186
  var notifyWorkspaceDisplayNamesListeners = (changedLineId) => {
@@ -18869,6 +19202,22 @@ var storeLineDisplayNames = (lineId, lineDisplayNamesMap) => {
18869
19202
  runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
18870
19203
  });
18871
19204
  };
19205
+ var storeGlobalDisplayNames = (displayNamesMap) => {
19206
+ runtimeWorkspaceDisplayNames[GLOBAL_CACHE_KEY] = {};
19207
+ displayNamesMap.forEach((displayName, workspaceId) => {
19208
+ runtimeWorkspaceDisplayNames[GLOBAL_CACHE_KEY][workspaceId] = displayName;
19209
+ });
19210
+ };
19211
+ var storeDisplayNamesByLine = (displayNamesByLine, requestedLineIds = []) => {
19212
+ displayNamesByLine.forEach((lineDisplayNamesMap, lineId) => {
19213
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
19214
+ });
19215
+ requestedLineIds.forEach((lineId) => {
19216
+ if (!runtimeWorkspaceDisplayNames[lineId]) {
19217
+ runtimeWorkspaceDisplayNames[lineId] = {};
19218
+ }
19219
+ });
19220
+ };
18872
19221
  var ensureLineWorkspaceDisplayNamesLoaded = async (lineId) => {
18873
19222
  if (!lineId || runtimeWorkspaceDisplayNames[lineId]) {
18874
19223
  return;
@@ -18952,7 +19301,11 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
18952
19301
  console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
18953
19302
  runtimeWorkspaceDisplayNames = {};
18954
19303
  lineLoadPromises.clear();
18955
- if (targetLineIds.length > 0) {
19304
+ if (targetLineIds.length > 1) {
19305
+ const displayNamesByLine = await workspaceService.getWorkspaceDisplayNamesByLine(void 0, targetLineIds);
19306
+ storeDisplayNamesByLine(displayNamesByLine, targetLineIds);
19307
+ console.log(`\u2705 Stored display names for ${displayNamesByLine.size} authorized lines`);
19308
+ } else if (targetLineIds.length === 1) {
18956
19309
  const results = await Promise.all(
18957
19310
  targetLineIds.map(async (lineId) => {
18958
19311
  console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
@@ -18967,10 +19320,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
18967
19320
  } else {
18968
19321
  console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
18969
19322
  const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
18970
- runtimeWorkspaceDisplayNames["global"] = {};
18971
- allWorkspacesMap.forEach((displayName, workspaceId) => {
18972
- runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
18973
- });
19323
+ storeGlobalDisplayNames(allWorkspacesMap);
18974
19324
  }
18975
19325
  isInitialized = true;
18976
19326
  initializedWithLineIds = targetLineIds;
@@ -18986,6 +19336,37 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
18986
19336
  })();
18987
19337
  await initializationPromise;
18988
19338
  }
19339
+ var preInitializeWorkspaceDisplayNamesForLines = async (lineIds) => {
19340
+ const uniqueLineIds = Array.from(new Set((lineIds || []).filter(Boolean)));
19341
+ if (uniqueLineIds.length === 0) {
19342
+ await preInitializeWorkspaceDisplayNames();
19343
+ return;
19344
+ }
19345
+ if (uniqueLineIds.length === 1) {
19346
+ await preInitializeWorkspaceDisplayNames(uniqueLineIds[0]);
19347
+ return;
19348
+ }
19349
+ if (isInitialized && uniqueLineIds.every((lineId) => Boolean(runtimeWorkspaceDisplayNames[lineId]))) {
19350
+ return;
19351
+ }
19352
+ if (initializationPromise) {
19353
+ await initializationPromise;
19354
+ if (uniqueLineIds.every((lineId) => Boolean(runtimeWorkspaceDisplayNames[lineId]))) {
19355
+ return;
19356
+ }
19357
+ }
19358
+ isInitializing = true;
19359
+ initializationPromise = workspaceService.getWorkspaceDisplayNamesByLine(void 0, uniqueLineIds).then((displayNamesByLine) => {
19360
+ storeDisplayNamesByLine(displayNamesByLine, uniqueLineIds);
19361
+ initializedWithLineIds = uniqueLineIds;
19362
+ isInitialized = true;
19363
+ notifyWorkspaceDisplayNamesListeners();
19364
+ }).finally(() => {
19365
+ isInitializing = false;
19366
+ initializationPromise = null;
19367
+ });
19368
+ await initializationPromise;
19369
+ };
18989
19370
  var preInitializeWorkspaceDisplayNames = async (lineId) => {
18990
19371
  console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
18991
19372
  if (isInitialized) {
@@ -38720,6 +39101,7 @@ var HourlyOutputChartComponent = ({
38720
39101
  timeRange: data2.timeRange,
38721
39102
  startTime,
38722
39103
  endTime,
39104
+ timezone,
38723
39105
  output: Math.round(data2.originalOutput || 0),
38724
39106
  target: data2.target,
38725
39107
  status: data2.status
@@ -38893,6 +39275,7 @@ var HourlyOutputChartComponent = ({
38893
39275
  timeRange: entry.timeRange,
38894
39276
  startTime,
38895
39277
  endTime,
39278
+ timezone,
38896
39279
  output: Math.round(entry.originalOutput || 0),
38897
39280
  target: entry.target,
38898
39281
  status: entry.status
@@ -42360,6 +42743,65 @@ var buildPrefetchedExplorerMetadata = (activeFilter, metadataCategoryId, categor
42360
42743
  };
42361
42744
  };
42362
42745
  var shouldDeferClipPlayerRender = (cropLoading, workspaceCrop) => cropLoading && workspaceCrop === null;
42746
+ var parseSortableCycleTime = (value) => {
42747
+ if (typeof value === "number" && Number.isFinite(value)) {
42748
+ return value;
42749
+ }
42750
+ if (typeof value === "string") {
42751
+ const parsed = Number(value);
42752
+ return Number.isFinite(parsed) ? parsed : null;
42753
+ }
42754
+ return null;
42755
+ };
42756
+ var readSortableCycleTime = (clip) => {
42757
+ const candidate = clip;
42758
+ return parseSortableCycleTime(candidate?.cycleTimeSeconds) ?? parseSortableCycleTime(candidate?.cycle_time_seconds) ?? parseSortableCycleTime(candidate?.duration) ?? parseSortableCycleTime(
42759
+ candidate?.original_task_metadata?.cycle_time
42760
+ ) ?? null;
42761
+ };
42762
+ var readSortableTimestamp = (clip) => {
42763
+ const candidate = clip;
42764
+ const timestamp = candidate?.creation_timestamp || candidate?.timestamp || candidate?.clip_timestamp || candidate?.clip_end_time || candidate?.clip_start_time;
42765
+ if (typeof timestamp !== "string") {
42766
+ return 0;
42767
+ }
42768
+ const parsed = new Date(timestamp).getTime();
42769
+ return Number.isFinite(parsed) ? parsed : 0;
42770
+ };
42771
+ var sortPercentileCycleClipsForDisplay = (categoryId, clips) => {
42772
+ if (categoryId !== "fast-cycles" && categoryId !== "slow-cycles") {
42773
+ return [...clips];
42774
+ }
42775
+ return [...clips].sort((left, right) => {
42776
+ const leftCycleTime = readSortableCycleTime(left);
42777
+ const rightCycleTime = readSortableCycleTime(right);
42778
+ const leftMissingCycleTime = leftCycleTime === null;
42779
+ const rightMissingCycleTime = rightCycleTime === null;
42780
+ if (leftMissingCycleTime !== rightMissingCycleTime) {
42781
+ return leftMissingCycleTime ? 1 : -1;
42782
+ }
42783
+ if (leftCycleTime !== null && rightCycleTime !== null && leftCycleTime !== rightCycleTime) {
42784
+ return categoryId === "fast-cycles" ? leftCycleTime - rightCycleTime : rightCycleTime - leftCycleTime;
42785
+ }
42786
+ return readSortableTimestamp(right) - readSortableTimestamp(left);
42787
+ });
42788
+ };
42789
+ var resolveInitialClipCategory = (categoryCandidates, clipTypes = [], counts = {}) => {
42790
+ const candidates = Array.from(
42791
+ new Set(
42792
+ categoryCandidates.filter((candidate) => Boolean(candidate)).map((candidate) => candidate.trim()).filter(Boolean)
42793
+ )
42794
+ );
42795
+ if (candidates.length === 0) {
42796
+ return null;
42797
+ }
42798
+ const availableCategories = /* @__PURE__ */ new Set();
42799
+ clipTypes.forEach((type) => {
42800
+ if (type?.type) availableCategories.add(String(type.type));
42801
+ if (type?.id) availableCategories.add(String(type.id));
42802
+ });
42803
+ return candidates.find((candidate) => (counts[candidate] || 0) > 0) || candidates.find((candidate) => availableCategories.has(candidate)) || null;
42804
+ };
42363
42805
  var getCategoryMetadataLoadPlanForFilterChange = ({
42364
42806
  activeFilter,
42365
42807
  currentClipId,
@@ -42371,6 +42813,12 @@ var getCategoryMetadataLoadPlanForFilterChange = ({
42371
42813
  if (activeFilter === "recent_flow_red_streak" && categoryTotal > 0) {
42372
42814
  return { shouldLoad: true, autoLoadFirstVideo: true };
42373
42815
  }
42816
+ if (activeFilter === "fast-cycles" || activeFilter === "slow-cycles") {
42817
+ return { shouldLoad: true, autoLoadFirstVideo: true };
42818
+ }
42819
+ if (categoryTotal > 0 && activeFilter === "cycle_completion") {
42820
+ return { shouldLoad: true, autoLoadFirstVideo: true };
42821
+ }
42374
42822
  return {
42375
42823
  shouldLoad: Boolean(currentClipId),
42376
42824
  autoLoadFirstVideo: false
@@ -46208,6 +46656,36 @@ var CLIP_METADATA_PAGE_SIZE = 50;
46208
46656
  var RECENT_FLOW_RED_STREAK_CLIP_TYPE2 = "recent_flow_red_streak";
46209
46657
  var RECENT_FLOW_RED_STREAK_DISPLAY_LABEL = "Low moments";
46210
46658
  var RECENT_FLOW_RED_STREAK_DISPLAY_SUBTITLE = "Moments of low efficiency";
46659
+ var REQUIRED_HOURLY_HANDOFF_CATEGORIES = {
46660
+ cycle_completion: {
46661
+ id: "cycle_completion",
46662
+ label: "Cycle Completion",
46663
+ description: "Successfully completed production cycles",
46664
+ color: "green",
46665
+ icon: "check-circle"
46666
+ },
46667
+ idle_time: {
46668
+ id: "idle_time",
46669
+ label: "Idle Time",
46670
+ description: "Idle periods",
46671
+ color: "purple",
46672
+ icon: "clock"
46673
+ },
46674
+ [RECENT_FLOW_RED_STREAK_CLIP_TYPE2]: {
46675
+ id: RECENT_FLOW_RED_STREAK_CLIP_TYPE2,
46676
+ label: RECENT_FLOW_RED_STREAK_DISPLAY_LABEL,
46677
+ description: RECENT_FLOW_RED_STREAK_DISPLAY_SUBTITLE,
46678
+ color: "red",
46679
+ icon: "alert-triangle"
46680
+ }
46681
+ };
46682
+ var REQUIRED_HOURLY_FILTER_CATEGORY_IDS = [
46683
+ RECENT_FLOW_RED_STREAK_CLIP_TYPE2,
46684
+ "cycle_completion",
46685
+ "fast-cycles",
46686
+ "slow-cycles",
46687
+ "idle_time"
46688
+ ];
46211
46689
  var parseCycleTime = (value) => {
46212
46690
  if (typeof value === "number" && Number.isFinite(value)) {
46213
46691
  return value;
@@ -46236,6 +46714,21 @@ var formatDurationLabel = (seconds) => {
46236
46714
  }
46237
46715
  return `${Math.round(roundedSeconds / 60)} min`;
46238
46716
  };
46717
+ var timeValueToMinutes = (value) => {
46718
+ const [hourValue, minuteValue] = value.substring(0, 5).split(":").map(Number);
46719
+ if (!Number.isInteger(hourValue) || !Number.isInteger(minuteValue) || hourValue < 0 || hourValue > 23 || minuteValue < 0 || minuteValue > 59) {
46720
+ return null;
46721
+ }
46722
+ return hourValue * 60 + minuteValue;
46723
+ };
46724
+ var isMinuteInTimeWindow = (minute, startValue, endValue) => {
46725
+ const startMinute = timeValueToMinutes(startValue);
46726
+ const endMinute = timeValueToMinutes(endValue);
46727
+ if (startMinute === null || endMinute === null) {
46728
+ return true;
46729
+ }
46730
+ return endMinute > startMinute ? minute >= startMinute && minute < endMinute : minute >= startMinute || minute < endMinute;
46731
+ };
46239
46732
  var sortRedFlowMetadata = (clips) => {
46240
46733
  return clips.slice().sort((left, right) => {
46241
46734
  const getOutputShortfall = (clip) => {
@@ -46260,6 +46753,27 @@ var sortRedFlowMetadata = (clips) => {
46260
46753
  return (Number.isFinite(rightTime) ? rightTime : 0) - (Number.isFinite(leftTime) ? leftTime : 0);
46261
46754
  });
46262
46755
  };
46756
+ var buildClipMetadataFromVideo = (video, index) => ({
46757
+ id: video.id,
46758
+ clipId: video.id,
46759
+ clip_timestamp: video.creation_timestamp || video.timestamp,
46760
+ description: video.description,
46761
+ severity: video.severity,
46762
+ category: video.type,
46763
+ duration: typeof video.duration === "number" ? video.duration : typeof video.cycle_time_seconds === "number" ? video.cycle_time_seconds : void 0,
46764
+ clip_start_time: video.clip_start_time,
46765
+ clip_end_time: video.clip_end_time,
46766
+ index,
46767
+ idle_start_time: video.idle_start_time,
46768
+ idle_end_time: video.idle_end_time,
46769
+ cycle_item_count: null,
46770
+ red_flow_timeline: video.red_flow_timeline,
46771
+ red_flow_severity_score: video.red_flow_severity_score,
46772
+ red_flow_output_shortfall_units: video.red_flow_output_shortfall_units,
46773
+ red_flow_worst_minute: video.red_flow_worst_minute,
46774
+ red_flow_explanation_summary: video.red_flow_explanation_summary,
46775
+ red_flow_explanation: video.red_flow_explanation
46776
+ });
46263
46777
  var getSeverityIcon = (severity, categoryId, cycleTimeSeconds, targetCycleTime, clipId) => {
46264
46778
  if (categoryId === "idle_time" || categoryId === "low_value" || categoryId === "longest-idles") {
46265
46779
  return null;
@@ -46338,6 +46852,8 @@ var FileManagerFilters = ({
46338
46852
  idleTimeVlmEnabled = false,
46339
46853
  showPercentileCycleFilters = true,
46340
46854
  prefetchedClipMetadata,
46855
+ prefetchedClipTotals,
46856
+ prefetchedPercentileClips,
46341
46857
  externallyManagedLoadingCategories,
46342
46858
  activeCategoryLoading,
46343
46859
  idleClipSort = "latest",
@@ -46345,6 +46861,7 @@ var FileManagerFilters = ({
46345
46861
  initialTimeFilter
46346
46862
  }) => {
46347
46863
  const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
46864
+ const [activeInitialTimeFilter, setActiveInitialTimeFilter] = useState(initialTimeFilter ?? null);
46348
46865
  const [startTime, setStartTime] = useState(initialTimeFilter?.startTime ?? "");
46349
46866
  const [endTime, setEndTime] = useState(initialTimeFilter?.endTime ?? "");
46350
46867
  const [isTimeFilterActive, setIsTimeFilterActive] = useState(
@@ -46359,14 +46876,93 @@ var FileManagerFilters = ({
46359
46876
  const [showIdleLabelFilterModal, setShowIdleLabelFilterModal] = useState(false);
46360
46877
  const [isLoadingIdleReasonOptions, setIsLoadingIdleReasonOptions] = useState(false);
46361
46878
  const timezone = useAppTimezone();
46879
+ const activeTimeFilterTimezone = activeInitialTimeFilter?.timezone || initialTimeFilter?.timezone || timezone;
46880
+ const activeTimeFilterKey = useMemo(() => startTime && endTime && isTimeFilterActive ? `${startTime}-${endTime}-${activeTimeFilterTimezone}` : "none", [activeTimeFilterTimezone, endTime, isTimeFilterActive, startTime]);
46881
+ const initialTimeFilterCategoryIds = useMemo(() => {
46882
+ if (!activeInitialTimeFilter) {
46883
+ return [];
46884
+ }
46885
+ return Array.from(new Set([
46886
+ activeInitialTimeFilter.categoryId,
46887
+ ...activeInitialTimeFilter.categoryIds || []
46888
+ ].filter((value) => typeof value === "string" && value.length > 0)));
46889
+ }, [activeInitialTimeFilter]);
46890
+ const requiredHourlyFilterCategoryIds = useMemo(() => {
46891
+ if (initialTimeFilterCategoryIds.length > 0) {
46892
+ return initialTimeFilterCategoryIds;
46893
+ }
46894
+ if (!isTimeFilterActive || !startTime || !endTime) {
46895
+ return [];
46896
+ }
46897
+ return REQUIRED_HOURLY_FILTER_CATEGORY_IDS;
46898
+ }, [endTime, initialTimeFilterCategoryIds, isTimeFilterActive, startTime]);
46899
+ const categoriesForTree = useMemo(() => {
46900
+ if (requiredHourlyFilterCategoryIds.length === 0) {
46901
+ return categories;
46902
+ }
46903
+ const existingCategoryIds = new Set(categories.map((category) => category.id));
46904
+ const mergedCategories = [...categories];
46905
+ requiredHourlyFilterCategoryIds.forEach((categoryId) => {
46906
+ const fallbackCategory = REQUIRED_HOURLY_HANDOFF_CATEGORIES[categoryId];
46907
+ if (!fallbackCategory || existingCategoryIds.has(categoryId)) {
46908
+ return;
46909
+ }
46910
+ mergedCategories.push(fallbackCategory);
46911
+ existingCategoryIds.add(categoryId);
46912
+ });
46913
+ return mergedCategories;
46914
+ }, [categories, requiredHourlyFilterCategoryIds]);
46362
46915
  const supabase = useSupabase();
46363
46916
  const [clipMetadata, setClipMetadata] = useState({});
46917
+ const [scopedCategoryTotals, setScopedCategoryTotals] = useState({});
46364
46918
  const [loadingCategories, setLoadingCategories] = useState(/* @__PURE__ */ new Set());
46919
+ const isCategoryScopedByTimeFilter = useCallback((categoryId) => {
46920
+ if (!startTime || !endTime || !isTimeFilterActive) {
46921
+ return false;
46922
+ }
46923
+ if (!activeInitialTimeFilter) {
46924
+ return true;
46925
+ }
46926
+ if (activeFilter === categoryId || activeInitialTimeFilter.categoryId === categoryId) {
46927
+ return true;
46928
+ }
46929
+ return Boolean(activeInitialTimeFilter.categoryIds?.includes(categoryId));
46930
+ }, [
46931
+ activeFilter,
46932
+ activeInitialTimeFilter,
46933
+ endTime,
46934
+ isTimeFilterActive,
46935
+ startTime
46936
+ ]);
46937
+ const isRequiredHourlyFilterCategory = useCallback((categoryId) => requiredHourlyFilterCategoryIds.includes(categoryId), [requiredHourlyFilterCategoryIds]);
46938
+ const manualHourlySnapshotKey = useMemo(() => {
46939
+ if (activeInitialTimeFilter || !isTimeFilterActive || !startTime || !endTime || !workspaceId || !date || shift === void 0) {
46940
+ return null;
46941
+ }
46942
+ return `${workspaceId}:${date}:${shift}:${startTime}:${endTime}:${activeTimeFilterTimezone}`;
46943
+ }, [
46944
+ activeInitialTimeFilter,
46945
+ activeTimeFilterTimezone,
46946
+ date,
46947
+ endTime,
46948
+ isTimeFilterActive,
46949
+ shift,
46950
+ startTime,
46951
+ workspaceId
46952
+ ]);
46953
+ const manualHourlySnapshotAppliedKeyRef = useRef(null);
46954
+ const manualHourlySnapshotInFlightKeyRef = useRef(null);
46955
+ const [manualHourlySnapshotFailureKey, setManualHourlySnapshotFailureKey] = useState(null);
46956
+ const isManualHourlySnapshotPending = Boolean(
46957
+ manualHourlySnapshotKey && manualHourlySnapshotAppliedKeyRef.current !== manualHourlySnapshotKey && manualHourlySnapshotFailureKey !== manualHourlySnapshotKey
46958
+ );
46365
46959
  const [categoryPages, setCategoryPages] = useState({});
46366
46960
  const [categoryHasMore, setCategoryHasMore] = useState({});
46367
46961
  const [localClipClassifications, setLocalClipClassifications] = useState({});
46368
46962
  const clipMetadataRef = useRef({});
46369
46963
  const inFlightMetadataRequestsRef = useRef(/* @__PURE__ */ new Set());
46964
+ const inFlightPercentileClipRequestsRef = useRef(/* @__PURE__ */ new Set());
46965
+ const autoSelectedScopedClipRef = useRef(null);
46370
46966
  const previousIdleClipSortRef = useRef(idleClipSort);
46371
46967
  const mergedClipClassifications = useMemo(() => ({
46372
46968
  ...clipClassifications || {},
@@ -46379,12 +46975,14 @@ var FileManagerFilters = ({
46379
46975
  if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime) {
46380
46976
  return;
46381
46977
  }
46978
+ setActiveInitialTimeFilter(initialTimeFilter);
46382
46979
  setStartTime(initialTimeFilter.startTime);
46383
46980
  setEndTime(initialTimeFilter.endTime);
46384
46981
  setIsTimeFilterActive(true);
46385
46982
  setShowTimeFilterModal(false);
46386
46983
  setStartSearchTerm("");
46387
46984
  setEndSearchTerm("");
46985
+ setScopedCategoryTotals({});
46388
46986
  }, [initialTimeFilter?.startTime, initialTimeFilter?.endTime]);
46389
46987
  useEffect(() => {
46390
46988
  if (previousIdleClipSortRef.current === idleClipSort) {
@@ -46416,18 +47014,66 @@ var FileManagerFilters = ({
46416
47014
  return next;
46417
47015
  });
46418
47016
  }, [idleClipSort]);
47017
+ useEffect(() => {
47018
+ manualHourlySnapshotAppliedKeyRef.current = null;
47019
+ manualHourlySnapshotInFlightKeyRef.current = null;
47020
+ setManualHourlySnapshotFailureKey(null);
47021
+ setScopedCategoryTotals({});
47022
+ setClipMetadata({});
47023
+ setCategoryPages({});
47024
+ setCategoryHasMore({});
47025
+ setPercentileClips({});
47026
+ setPercentileCounts({
47027
+ "fast-cycles": null,
47028
+ "slow-cycles": null
47029
+ });
47030
+ }, [activeTimeFilterKey]);
46419
47031
  const isCategoryExternallyManaged = useCallback((categoryId) => {
46420
47032
  if (!categoryId) {
46421
47033
  return false;
46422
47034
  }
46423
- if (prefetchedClipMetadata && Array.isArray(prefetchedClipMetadata[categoryId]) && prefetchedClipMetadata[categoryId].length > 0) {
47035
+ if (isManualHourlySnapshotPending && requiredHourlyFilterCategoryIds.includes(categoryId)) {
47036
+ return true;
47037
+ }
47038
+ if (prefetchedClipMetadata && Array.isArray(prefetchedClipMetadata[categoryId]) && (prefetchedClipMetadata[categoryId].length > 0 || typeof prefetchedClipTotals?.[categoryId] === "number")) {
47039
+ return true;
47040
+ }
47041
+ if (prefetchedPercentileClips && Array.isArray(prefetchedPercentileClips[categoryId]) && (prefetchedPercentileClips[categoryId].length > 0 || isCategoryScopedByTimeFilter(categoryId) && typeof prefetchedClipTotals?.[categoryId] === "number")) {
46424
47042
  return true;
46425
47043
  }
46426
47044
  if (externallyManagedLoadingCategories?.[categoryId]) {
46427
47045
  return true;
46428
47046
  }
46429
47047
  return false;
46430
- }, [prefetchedClipMetadata, externallyManagedLoadingCategories]);
47048
+ }, [
47049
+ externallyManagedLoadingCategories,
47050
+ isManualHourlySnapshotPending,
47051
+ isCategoryScopedByTimeFilter,
47052
+ prefetchedClipMetadata,
47053
+ prefetchedClipTotals,
47054
+ prefetchedPercentileClips,
47055
+ requiredHourlyFilterCategoryIds
47056
+ ]);
47057
+ const getAvailableClipMetadata = useCallback((categoryId) => {
47058
+ const localClips = clipMetadata[categoryId];
47059
+ if (Array.isArray(localClips) && localClips.length > 0) {
47060
+ return localClips;
47061
+ }
47062
+ const prefetchedClips = prefetchedClipMetadata?.[categoryId];
47063
+ if (Array.isArray(prefetchedClips) && prefetchedClips.length > 0) {
47064
+ return prefetchedClips;
47065
+ }
47066
+ return [];
47067
+ }, [clipMetadata, prefetchedClipMetadata]);
47068
+ const hasKnownClipMetadata = useCallback((categoryId) => {
47069
+ if (Array.isArray(clipMetadata[categoryId])) {
47070
+ return true;
47071
+ }
47072
+ if (Array.isArray(prefetchedClipMetadata?.[categoryId])) {
47073
+ return true;
47074
+ }
47075
+ return typeof prefetchedClipTotals?.[categoryId] === "number";
47076
+ }, [clipMetadata, prefetchedClipMetadata, prefetchedClipTotals]);
46431
47077
  useEffect(() => {
46432
47078
  if (!prefetchedClipMetadata) {
46433
47079
  return;
@@ -46450,6 +47096,22 @@ var FileManagerFilters = ({
46450
47096
  });
46451
47097
  return changed ? next : prev;
46452
47098
  });
47099
+ setScopedCategoryTotals((prev) => {
47100
+ let changed = false;
47101
+ const next = { ...prev };
47102
+ Object.entries(prefetchedClipMetadata).forEach(([categoryId, clips]) => {
47103
+ if (!Array.isArray(clips) || !isCategoryScopedByTimeFilter(categoryId)) {
47104
+ return;
47105
+ }
47106
+ const nextTotal = typeof prefetchedClipTotals?.[categoryId] === "number" ? Math.max(0, prefetchedClipTotals[categoryId]) : clips.length;
47107
+ if (next[categoryId] === nextTotal) {
47108
+ return;
47109
+ }
47110
+ next[categoryId] = nextTotal;
47111
+ changed = true;
47112
+ });
47113
+ return changed ? next : prev;
47114
+ });
46453
47115
  setCategoryPages((prev) => {
46454
47116
  let changed = false;
46455
47117
  const next = { ...prev };
@@ -46474,7 +47136,7 @@ var FileManagerFilters = ({
46474
47136
  return;
46475
47137
  }
46476
47138
  const knownTotal = typeof counts?.[categoryId] === "number" ? counts[categoryId] : null;
46477
- const inferredHasMore = knownTotal !== null ? clips.length < knownTotal : prev[categoryId];
47139
+ const inferredHasMore = isCategoryScopedByTimeFilter(categoryId) ? false : knownTotal !== null ? clips.length < knownTotal : prev[categoryId];
46478
47140
  if (typeof inferredHasMore !== "boolean" || next[categoryId] === inferredHasMore) {
46479
47141
  return;
46480
47142
  }
@@ -46483,8 +47145,68 @@ var FileManagerFilters = ({
46483
47145
  });
46484
47146
  return changed ? next : prev;
46485
47147
  });
46486
- }, [prefetchedClipMetadata, counts]);
47148
+ }, [prefetchedClipMetadata, prefetchedClipTotals, counts, isCategoryScopedByTimeFilter]);
47149
+ useEffect(() => {
47150
+ if (!prefetchedPercentileClips) {
47151
+ return;
47152
+ }
47153
+ setPercentileClips((prev) => {
47154
+ let changed = false;
47155
+ const next = { ...prev };
47156
+ ["fast-cycles", "slow-cycles"].forEach((categoryId) => {
47157
+ const clips = prefetchedPercentileClips[categoryId];
47158
+ if (!Array.isArray(clips)) {
47159
+ return;
47160
+ }
47161
+ const previousClips = prev[categoryId] || [];
47162
+ const previousSignature = previousClips.map((clip) => clip.id).join("|");
47163
+ const nextSignature = clips.map((clip) => clip.id).join("|");
47164
+ if (previousSignature === nextSignature) {
47165
+ return;
47166
+ }
47167
+ next[categoryId] = clips;
47168
+ changed = true;
47169
+ });
47170
+ return changed ? next : prev;
47171
+ });
47172
+ setPercentileCounts((prev) => {
47173
+ let changed = false;
47174
+ const next = { ...prev };
47175
+ ["fast-cycles", "slow-cycles"].forEach((categoryId) => {
47176
+ const clips = prefetchedPercentileClips[categoryId];
47177
+ if (!Array.isArray(clips)) {
47178
+ return;
47179
+ }
47180
+ const nextTotal = typeof prefetchedClipTotals?.[categoryId] === "number" ? Math.max(0, prefetchedClipTotals[categoryId]) : clips.length;
47181
+ if (next[categoryId] === nextTotal) {
47182
+ return;
47183
+ }
47184
+ next[categoryId] = nextTotal;
47185
+ changed = true;
47186
+ });
47187
+ return changed ? next : prev;
47188
+ });
47189
+ }, [prefetchedClipTotals, prefetchedPercentileClips]);
46487
47190
  const { state: filterState } = useClipFilter();
47191
+ const shouldShowCategory = useCallback((categoryId) => {
47192
+ switch (categoryId) {
47193
+ case "fast-cycles":
47194
+ return showPercentileCycleFilters && filterState.showFastCycles;
47195
+ case "slow-cycles":
47196
+ return showPercentileCycleFilters && filterState.showSlowCycles;
47197
+ case "longest-idles":
47198
+ return false;
47199
+ // filterState.showLongestIdles; // Temporarily disabled
47200
+ case "cycle_completion":
47201
+ return filterState.showCycleCompletion;
47202
+ case "idle_time":
47203
+ return filterState.showIdleTime;
47204
+ case "sop_deviations":
47205
+ return filterState.showSopDeviations;
47206
+ default:
47207
+ return true;
47208
+ }
47209
+ }, [filterState, showPercentileCycleFilters]);
46488
47210
  const [percentileCounts, setPercentileCounts] = useState({
46489
47211
  "fast-cycles": null,
46490
47212
  "slow-cycles": null
@@ -46594,11 +47316,12 @@ var FileManagerFilters = ({
46594
47316
  return null;
46595
47317
  }
46596
47318
  }, [supabase]);
46597
- const getMetadataLoadingKey = useCallback((categoryId, page) => `${categoryId}-${page}-${categoryId === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"}`, [idleClipSort]);
47319
+ const getMetadataLoadingKey = useCallback((categoryId, page) => `${categoryId}-${page}-${categoryId === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"}-${activeTimeFilterKey}`, [activeTimeFilterKey, idleClipSort]);
46598
47320
  const fetchClipMetadataPage = useCallback(async (categoryId, page = 1) => {
46599
47321
  if (!workspaceId || !date || shift === void 0) {
46600
47322
  throw new Error("Missing required params for clip metadata fetch");
46601
47323
  }
47324
+ const shouldScopeToTimeFilter = isCategoryScopedByTimeFilter(categoryId);
46602
47325
  const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
46603
47326
  method: "POST",
46604
47327
  headers: {
@@ -46612,9 +47335,12 @@ var FileManagerFilters = ({
46612
47335
  category: categoryId,
46613
47336
  page,
46614
47337
  limit: CLIP_METADATA_PAGE_SIZE,
46615
- knownTotal: typeof counts?.[categoryId] === "number" ? counts[categoryId] : null,
47338
+ knownTotal: shouldScopeToTimeFilter ? null : typeof counts?.[categoryId] === "number" ? counts[categoryId] : null,
46616
47339
  snapshotDateTime,
46617
47340
  snapshotClipId,
47341
+ startTime: shouldScopeToTimeFilter ? startTime : void 0,
47342
+ endTime: shouldScopeToTimeFilter ? endTime : void 0,
47343
+ timeFilterTimezone: shouldScopeToTimeFilter ? activeTimeFilterTimezone : void 0,
46618
47344
  sort: categoryId === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
46619
47345
  }),
46620
47346
  redirectReason: "session_expired"
@@ -46623,7 +47349,20 @@ var FileManagerFilters = ({
46623
47349
  throw new Error(`API error: ${response.status}`);
46624
47350
  }
46625
47351
  return response.json();
46626
- }, [workspaceId, date, shift, counts, snapshotDateTime, snapshotClipId, idleClipSort, supabase]);
47352
+ }, [
47353
+ activeTimeFilterTimezone,
47354
+ endTime,
47355
+ isCategoryScopedByTimeFilter,
47356
+ startTime,
47357
+ workspaceId,
47358
+ date,
47359
+ shift,
47360
+ counts,
47361
+ snapshotDateTime,
47362
+ snapshotClipId,
47363
+ idleClipSort,
47364
+ supabase
47365
+ ]);
46627
47366
  const seedIdleClassifications = useCallback(async (clips) => {
46628
47367
  if (!idleTimeVlmEnabled || clips.length === 0) {
46629
47368
  return;
@@ -46691,6 +47430,12 @@ var FileManagerFilters = ({
46691
47430
  ...prev,
46692
47431
  [categoryId]: page === 1 ? data.clips : [...prev[categoryId] || [], ...data.clips]
46693
47432
  }));
47433
+ if (isCategoryScopedByTimeFilter(categoryId)) {
47434
+ setScopedCategoryTotals((prev) => ({
47435
+ ...prev,
47436
+ [categoryId]: Math.max(0, Number(data.total || 0))
47437
+ }));
47438
+ }
46694
47439
  if (categoryId === "idle_time" && idleTimeVlmEnabled) {
46695
47440
  await seedIdleClassifications(data.clips || []);
46696
47441
  }
@@ -46707,7 +47452,94 @@ var FileManagerFilters = ({
46707
47452
  return newSet;
46708
47453
  });
46709
47454
  }
46710
- }, [workspaceId, date, shift, fetchClipMetadataPage, idleTimeVlmEnabled, seedIdleClassifications, getMetadataLoadingKey]);
47455
+ }, [workspaceId, date, shift, fetchClipMetadataPage, idleTimeVlmEnabled, seedIdleClassifications, getMetadataLoadingKey, isCategoryScopedByTimeFilter]);
47456
+ useCallback(async (categoryId) => {
47457
+ if (!workspaceId || !date || shift === void 0) {
47458
+ console.warn("[FileManager] Missing required params for full clip metadata fetch");
47459
+ return;
47460
+ }
47461
+ const loadingKey = `${getMetadataLoadingKey(categoryId, 1)}-all`;
47462
+ if (inFlightMetadataRequestsRef.current.has(loadingKey)) {
47463
+ return;
47464
+ }
47465
+ inFlightMetadataRequestsRef.current.add(loadingKey);
47466
+ setLoadingCategories((prev) => /* @__PURE__ */ new Set([...prev, loadingKey]));
47467
+ try {
47468
+ let accumulatedClips = [];
47469
+ let currentPage = 0;
47470
+ let hasMore = true;
47471
+ while (hasMore) {
47472
+ const nextPage = currentPage + 1;
47473
+ const pageData = await fetchClipMetadataPage(categoryId, nextPage);
47474
+ const pageClips = pageData.clips || [];
47475
+ accumulatedClips = [...accumulatedClips, ...pageClips];
47476
+ currentPage = nextPage;
47477
+ hasMore = Boolean(pageData.hasMore && pageClips.length > 0);
47478
+ }
47479
+ const dedupedClips = accumulatedClips.filter((clip, index, arr) => {
47480
+ const clipKey = clip.clipId || clip.id;
47481
+ return arr.findIndex((item) => (item.clipId || item.id) === clipKey) === index;
47482
+ });
47483
+ setClipMetadata((prev) => ({
47484
+ ...prev,
47485
+ [categoryId]: dedupedClips
47486
+ }));
47487
+ setCategoryPages((prev) => ({ ...prev, [categoryId]: Math.max(currentPage, 1) }));
47488
+ setCategoryHasMore((prev) => ({ ...prev, [categoryId]: false }));
47489
+ if (categoryId === "idle_time" && idleTimeVlmEnabled) {
47490
+ await seedIdleClassifications(dedupedClips);
47491
+ }
47492
+ console.log(`[FileManager] Loaded all ${dedupedClips.length} clips for ${categoryId} due to chart time filter`);
47493
+ } catch (error) {
47494
+ console.error("[FileManager] Error fetching full clip metadata:", error);
47495
+ } finally {
47496
+ inFlightMetadataRequestsRef.current.delete(loadingKey);
47497
+ setLoadingCategories((prev) => {
47498
+ const newSet = new Set(prev);
47499
+ newSet.delete(loadingKey);
47500
+ return newSet;
47501
+ });
47502
+ }
47503
+ }, [
47504
+ workspaceId,
47505
+ date,
47506
+ shift,
47507
+ fetchClipMetadataPage,
47508
+ idleTimeVlmEnabled,
47509
+ seedIdleClassifications,
47510
+ getMetadataLoadingKey
47511
+ ]);
47512
+ const isInitialTimeFilterCategory = useCallback((categoryId) => {
47513
+ if (!startTime || !endTime || !activeInitialTimeFilter) {
47514
+ return false;
47515
+ }
47516
+ if (activeFilter === categoryId) {
47517
+ return true;
47518
+ }
47519
+ if (activeInitialTimeFilter.categoryId === categoryId) {
47520
+ return true;
47521
+ }
47522
+ return Array.isArray(activeInitialTimeFilter.categoryIds) && activeInitialTimeFilter.categoryIds.includes(categoryId);
47523
+ }, [
47524
+ activeFilter,
47525
+ activeInitialTimeFilter,
47526
+ endTime,
47527
+ startTime
47528
+ ]);
47529
+ const hasCompleteMetadataForInitialTimeFilter = useCallback((categoryId) => {
47530
+ if (!isInitialTimeFilterCategory(categoryId)) {
47531
+ return true;
47532
+ }
47533
+ const loadedClips = clipMetadataRef.current[categoryId] || [];
47534
+ const knownTotal = typeof scopedCategoryTotals[categoryId] === "number" ? scopedCategoryTotals[categoryId] : null;
47535
+ if (categoryHasMore[categoryId]) {
47536
+ return false;
47537
+ }
47538
+ if (knownTotal !== null && loadedClips.length < knownTotal) {
47539
+ return false;
47540
+ }
47541
+ return loadedClips.length > 0 || knownTotal === 0;
47542
+ }, [categoryHasMore, isInitialTimeFilterCategory, scopedCategoryTotals]);
46711
47543
  const ensureAllIdleTimeClipMetadataLoaded = useCallback(async () => {
46712
47544
  if (!workspaceId || !date || shift === void 0) {
46713
47545
  return;
@@ -46773,6 +47605,12 @@ var FileManagerFilters = ({
46773
47605
  console.warn("[FileManager] Missing required params for percentile clips fetch");
46774
47606
  return;
46775
47607
  }
47608
+ const shouldScopeToTimeFilter = isCategoryScopedByTimeFilter(type);
47609
+ const requestKey = `${type}:${date}:${shift}:${filterState.percentile}:${shouldScopeToTimeFilter ? activeTimeFilterKey : "unscoped"}`;
47610
+ if (inFlightPercentileClipRequestsRef.current.has(requestKey)) {
47611
+ return;
47612
+ }
47613
+ inFlightPercentileClipRequestsRef.current.add(requestKey);
46776
47614
  try {
46777
47615
  const startDate = `${date}T00:00:00Z`;
46778
47616
  const endDate = `${date}T23:59:59Z`;
@@ -46788,7 +47626,10 @@ var FileManagerFilters = ({
46788
47626
  endDate,
46789
47627
  percentile: filterState.percentile,
46790
47628
  shiftId: shift,
46791
- limit: 50,
47629
+ limit: shouldScopeToTimeFilter ? 500 : 100,
47630
+ startTime: shouldScopeToTimeFilter ? startTime : void 0,
47631
+ endTime: shouldScopeToTimeFilter ? endTime : void 0,
47632
+ timeFilterTimezone: shouldScopeToTimeFilter ? activeTimeFilterTimezone : void 0,
46792
47633
  // The actual percentile action (fast-cycles, slow-cycles, idle-times)
46793
47634
  percentileAction: type
46794
47635
  }),
@@ -46803,7 +47644,7 @@ var FileManagerFilters = ({
46803
47644
  [type]: data.clips || []
46804
47645
  }));
46805
47646
  setPercentileCounts((prev) => {
46806
- if (typeof prev[type] === "number") {
47647
+ if (!shouldScopeToTimeFilter && typeof prev[type] === "number") {
46807
47648
  return prev;
46808
47649
  }
46809
47650
  return {
@@ -46814,8 +47655,206 @@ var FileManagerFilters = ({
46814
47655
  console.log(`[FileManager] Loaded ${data.clips?.length || 0} ${type} clips (percentile: ${filterState.percentile}%)`);
46815
47656
  } catch (error) {
46816
47657
  console.error(`[FileManager] Error fetching ${type} clips:`, error);
47658
+ } finally {
47659
+ inFlightPercentileClipRequestsRef.current.delete(requestKey);
47660
+ }
47661
+ }, [
47662
+ activeTimeFilterTimezone,
47663
+ activeTimeFilterKey,
47664
+ endTime,
47665
+ isCategoryScopedByTimeFilter,
47666
+ startTime,
47667
+ workspaceId,
47668
+ date,
47669
+ shift,
47670
+ filterState.percentile,
47671
+ showPercentileCycleFilters,
47672
+ supabase
47673
+ ]);
47674
+ useEffect(() => {
47675
+ if (!manualHourlySnapshotKey) {
47676
+ return;
47677
+ }
47678
+ if (manualHourlySnapshotAppliedKeyRef.current === manualHourlySnapshotKey) {
47679
+ return;
47680
+ }
47681
+ if (manualHourlySnapshotInFlightKeyRef.current === manualHourlySnapshotKey) {
47682
+ return;
47683
+ }
47684
+ manualHourlySnapshotInFlightKeyRef.current = manualHourlySnapshotKey;
47685
+ setManualHourlySnapshotFailureKey((prev) => prev === manualHourlySnapshotKey ? null : prev);
47686
+ const applyHourlySnapshot = async () => {
47687
+ try {
47688
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
47689
+ method: "POST",
47690
+ headers: {
47691
+ "Content-Type": "application/json"
47692
+ },
47693
+ body: JSON.stringify({
47694
+ action: "hourly-snapshot",
47695
+ workspaceId,
47696
+ date,
47697
+ shift,
47698
+ startTime,
47699
+ endTime,
47700
+ timeFilterTimezone: activeTimeFilterTimezone
47701
+ }),
47702
+ redirectReason: "session_expired"
47703
+ });
47704
+ if (!response.ok) {
47705
+ throw new Error(`API error: ${response.status}`);
47706
+ }
47707
+ const snapshot = await response.json();
47708
+ const metadataByCategory = snapshot?.metadataByCategory && typeof snapshot.metadataByCategory === "object" ? snapshot.metadataByCategory : {};
47709
+ const percentileClipsByCategory = snapshot?.percentileClipsByCategory && typeof snapshot.percentileClipsByCategory === "object" ? snapshot.percentileClipsByCategory : {};
47710
+ const rawTotalsByCategory = snapshot?.totalsByCategory && typeof snapshot.totalsByCategory === "object" ? snapshot.totalsByCategory : {};
47711
+ const totalsByCategory = requiredHourlyFilterCategoryIds.reduce((accumulator, categoryId) => {
47712
+ const rawTotal = rawTotalsByCategory[categoryId];
47713
+ if (typeof rawTotal === "number" && Number.isFinite(rawTotal)) {
47714
+ accumulator[categoryId] = Math.max(0, rawTotal);
47715
+ return accumulator;
47716
+ }
47717
+ if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
47718
+ accumulator[categoryId] = Array.isArray(percentileClipsByCategory[categoryId]) ? percentileClipsByCategory[categoryId].length : 0;
47719
+ return accumulator;
47720
+ }
47721
+ accumulator[categoryId] = Array.isArray(metadataByCategory[categoryId]) ? metadataByCategory[categoryId].length : 0;
47722
+ return accumulator;
47723
+ }, {});
47724
+ setClipMetadata((prev) => {
47725
+ const next = { ...prev };
47726
+ requiredHourlyFilterCategoryIds.forEach((categoryId) => {
47727
+ if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
47728
+ return;
47729
+ }
47730
+ next[categoryId] = Array.isArray(metadataByCategory[categoryId]) ? metadataByCategory[categoryId] : [];
47731
+ });
47732
+ return next;
47733
+ });
47734
+ setScopedCategoryTotals((prev) => ({
47735
+ ...prev,
47736
+ ...totalsByCategory
47737
+ }));
47738
+ if (showPercentileCycleFilters) {
47739
+ setPercentileClips((prev) => ({
47740
+ ...prev,
47741
+ "fast-cycles": Array.isArray(percentileClipsByCategory["fast-cycles"]) ? percentileClipsByCategory["fast-cycles"] : [],
47742
+ "slow-cycles": Array.isArray(percentileClipsByCategory["slow-cycles"]) ? percentileClipsByCategory["slow-cycles"] : []
47743
+ }));
47744
+ setPercentileCounts((prev) => ({
47745
+ ...prev,
47746
+ "fast-cycles": totalsByCategory["fast-cycles"] ?? 0,
47747
+ "slow-cycles": totalsByCategory["slow-cycles"] ?? 0
47748
+ }));
47749
+ }
47750
+ setCategoryPages((prev) => {
47751
+ const next = { ...prev };
47752
+ requiredHourlyFilterCategoryIds.forEach((categoryId) => {
47753
+ next[categoryId] = 1;
47754
+ });
47755
+ return next;
47756
+ });
47757
+ setCategoryHasMore((prev) => {
47758
+ const next = { ...prev };
47759
+ requiredHourlyFilterCategoryIds.forEach((categoryId) => {
47760
+ next[categoryId] = false;
47761
+ });
47762
+ return next;
47763
+ });
47764
+ const idleClips = metadataByCategory.idle_time;
47765
+ if (Array.isArray(idleClips) && idleClips.length > 0) {
47766
+ await seedIdleClassifications(idleClips);
47767
+ }
47768
+ manualHourlySnapshotAppliedKeyRef.current = manualHourlySnapshotKey;
47769
+ setManualHourlySnapshotFailureKey((prev) => prev === manualHourlySnapshotKey ? null : prev);
47770
+ } catch (error) {
47771
+ console.error("[FileManager] Error fetching manual hourly snapshot:", error);
47772
+ setManualHourlySnapshotFailureKey(manualHourlySnapshotKey);
47773
+ } finally {
47774
+ if (manualHourlySnapshotInFlightKeyRef.current === manualHourlySnapshotKey) {
47775
+ manualHourlySnapshotInFlightKeyRef.current = null;
47776
+ }
47777
+ }
47778
+ };
47779
+ void applyHourlySnapshot();
47780
+ }, [
47781
+ activeTimeFilterTimezone,
47782
+ date,
47783
+ endTime,
47784
+ manualHourlySnapshotKey,
47785
+ requiredHourlyFilterCategoryIds,
47786
+ seedIdleClassifications,
47787
+ shift,
47788
+ showPercentileCycleFilters,
47789
+ startTime,
47790
+ supabase,
47791
+ workspaceId
47792
+ ]);
47793
+ useEffect(() => {
47794
+ if (!startTime || !endTime || !activeInitialTimeFilter || initialTimeFilterCategoryIds.length === 0) {
47795
+ return;
46817
47796
  }
46818
- }, [workspaceId, date, shift, filterState.percentile, showPercentileCycleFilters, supabase]);
47797
+ initialTimeFilterCategoryIds.forEach((categoryId) => {
47798
+ if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
47799
+ const hasScopedPercentileResult = typeof percentileCounts[categoryId] === "number";
47800
+ if (!isCategoryExternallyManaged(categoryId) && !hasScopedPercentileResult && showPercentileCycleFilters) {
47801
+ fetchPercentileClips(categoryId);
47802
+ }
47803
+ return;
47804
+ }
47805
+ if (isCategoryExternallyManaged(categoryId)) {
47806
+ return;
47807
+ }
47808
+ if (!hasCompleteMetadataForInitialTimeFilter(categoryId)) {
47809
+ fetchClipMetadata(categoryId, 1);
47810
+ }
47811
+ });
47812
+ }, [
47813
+ activeInitialTimeFilter,
47814
+ endTime,
47815
+ fetchClipMetadata,
47816
+ fetchPercentileClips,
47817
+ hasCompleteMetadataForInitialTimeFilter,
47818
+ initialTimeFilterCategoryIds,
47819
+ isCategoryExternallyManaged,
47820
+ percentileCounts,
47821
+ showPercentileCycleFilters,
47822
+ startTime
47823
+ ]);
47824
+ useEffect(() => {
47825
+ if (!startTime || !endTime || !isTimeFilterActive || activeInitialTimeFilter) {
47826
+ return;
47827
+ }
47828
+ categoriesForTree.forEach((category) => {
47829
+ if (!shouldShowCategory(category.id) || isCategoryExternallyManaged(category.id)) {
47830
+ return;
47831
+ }
47832
+ if (typeof scopedCategoryTotals[category.id] !== "number") {
47833
+ fetchClipMetadata(category.id, 1);
47834
+ }
47835
+ });
47836
+ if (showPercentileCycleFilters) {
47837
+ if (!isCategoryExternallyManaged("fast-cycles") && typeof percentileCounts["fast-cycles"] !== "number") {
47838
+ fetchPercentileClips("fast-cycles");
47839
+ }
47840
+ if (!isCategoryExternallyManaged("slow-cycles") && typeof percentileCounts["slow-cycles"] !== "number") {
47841
+ fetchPercentileClips("slow-cycles");
47842
+ }
47843
+ }
47844
+ }, [
47845
+ activeInitialTimeFilter,
47846
+ categoriesForTree,
47847
+ endTime,
47848
+ fetchClipMetadata,
47849
+ fetchPercentileClips,
47850
+ isCategoryExternallyManaged,
47851
+ isTimeFilterActive,
47852
+ percentileCounts,
47853
+ scopedCategoryTotals,
47854
+ shouldShowCategory,
47855
+ showPercentileCycleFilters,
47856
+ startTime
47857
+ ]);
46819
47858
  const percentileCountsKey = useMemo(() => {
46820
47859
  if (!workspaceId || !date || shift === void 0) {
46821
47860
  return null;
@@ -46838,12 +47877,15 @@ var FileManagerFilters = ({
46838
47877
  }
46839
47878
  percentileCountsKeyRef.current = percentileCountsKey;
46840
47879
  percentilePrefetchRef.current = { key: null, types: /* @__PURE__ */ new Set() };
47880
+ if (prefetchedPercentileClips && (Array.isArray(prefetchedPercentileClips["fast-cycles"]) || Array.isArray(prefetchedPercentileClips["slow-cycles"]))) {
47881
+ return;
47882
+ }
46841
47883
  setPercentileCounts({
46842
47884
  "fast-cycles": null,
46843
47885
  "slow-cycles": null
46844
47886
  });
46845
47887
  setPercentileClips({});
46846
- }, [showPercentileCycleFilters, percentileCountsKey]);
47888
+ }, [prefetchedPercentileClips, showPercentileCycleFilters, percentileCountsKey]);
46847
47889
  useEffect(() => {
46848
47890
  if (!showPercentileCycleFilters) {
46849
47891
  return;
@@ -46899,11 +47941,20 @@ var FileManagerFilters = ({
46899
47941
  const data = await response.json();
46900
47942
  const fastCycles = data?.counts?.["fast-cycles"];
46901
47943
  const slowCycles = data?.counts?.["slow-cycles"];
46902
- setPercentileCounts((prev) => ({
46903
- ...prev,
46904
- "fast-cycles": typeof fastCycles === "number" ? fastCycles : prev["fast-cycles"],
46905
- "slow-cycles": typeof slowCycles === "number" ? slowCycles : prev["slow-cycles"]
46906
- }));
47944
+ if (typeof fastCycles === "number" || typeof slowCycles === "number") {
47945
+ setPercentileCounts((prev) => {
47946
+ const nextFastCycles = typeof fastCycles === "number" ? fastCycles : prev["fast-cycles"];
47947
+ const nextSlowCycles = typeof slowCycles === "number" ? slowCycles : prev["slow-cycles"];
47948
+ if (prev["fast-cycles"] === nextFastCycles && prev["slow-cycles"] === nextSlowCycles) {
47949
+ return prev;
47950
+ }
47951
+ return {
47952
+ ...prev,
47953
+ "fast-cycles": nextFastCycles,
47954
+ "slow-cycles": nextSlowCycles
47955
+ };
47956
+ });
47957
+ }
46907
47958
  if (options?.prefetchClips) {
46908
47959
  if (percentilePrefetchRef.current.key !== requestKey) {
46909
47960
  percentilePrefetchRef.current = { key: requestKey, types: /* @__PURE__ */ new Set() };
@@ -46922,37 +47973,30 @@ var FileManagerFilters = ({
46922
47973
  }
46923
47974
  }, [workspaceId, date, shift, filterState.percentile, showPercentileCycleFilters, supabase, percentileCounts, percentileClips, fetchPercentileClips]);
46924
47975
  useEffect(() => {
46925
- if (!showPercentileCycleFilters || !isReady || !percentileCountsKey) {
47976
+ if (!showPercentileCycleFilters || !isReady || !percentileCountsKey || isTimeFilterActive) {
46926
47977
  return;
46927
47978
  }
46928
- const schedule = () => {
46929
- fetchPercentileCounts({ prefetchClips: true });
46930
- };
46931
- if (typeof window !== "undefined" && "requestIdleCallback" in window) {
46932
- window.requestIdleCallback(schedule, { timeout: 1e3 });
46933
- } else {
46934
- setTimeout(schedule, 0);
46935
- }
46936
- }, [showPercentileCycleFilters, isReady, percentileCountsKey, fetchPercentileCounts]);
46937
- const shouldShowCategory = useCallback((categoryId) => {
46938
- switch (categoryId) {
46939
- case "fast-cycles":
46940
- return showPercentileCycleFilters && filterState.showFastCycles;
46941
- case "slow-cycles":
46942
- return showPercentileCycleFilters && filterState.showSlowCycles;
46943
- case "longest-idles":
46944
- return false;
46945
- // filterState.showLongestIdles; // Temporarily disabled
46946
- case "cycle_completion":
46947
- return filterState.showCycleCompletion;
46948
- case "idle_time":
46949
- return filterState.showIdleTime;
46950
- case "sop_deviations":
46951
- return filterState.showSopDeviations;
46952
- default:
46953
- return true;
47979
+ fetchPercentileCounts({ prefetchClips: true });
47980
+ }, [showPercentileCycleFilters, isReady, percentileCountsKey, fetchPercentileCounts, isTimeFilterActive]);
47981
+ useEffect(() => {
47982
+ if (!isReady || isTimeFilterActive || activeFilter !== RECENT_FLOW_RED_STREAK_CLIP_TYPE2) {
47983
+ return;
46954
47984
  }
46955
- }, [filterState, showPercentileCycleFilters]);
47985
+ ["cycle_completion", "idle_time"].forEach((categoryId) => {
47986
+ if ((counts?.[categoryId] || 0) <= 0 || hasKnownClipMetadata(categoryId) || isCategoryExternallyManaged(categoryId)) {
47987
+ return;
47988
+ }
47989
+ fetchClipMetadata(categoryId, 1);
47990
+ });
47991
+ }, [
47992
+ activeFilter,
47993
+ counts,
47994
+ fetchClipMetadata,
47995
+ hasKnownClipMetadata,
47996
+ isCategoryExternallyManaged,
47997
+ isReady,
47998
+ isTimeFilterActive
47999
+ ]);
46956
48000
  const getPercentileIcon = useCallback((type, isExpanded, colorClasses) => {
46957
48001
  const iconMap = {
46958
48002
  "fast-cycles": { icon: TrendingUp, color: "text-green-600" },
@@ -46981,12 +48025,29 @@ var FileManagerFilters = ({
46981
48025
  newExpanded.add(activeFilter);
46982
48026
  return newExpanded;
46983
48027
  });
46984
- const category = categories.find((cat) => cat.id === activeFilter);
46985
- if (category && !isCategoryExternallyManaged(activeFilter) && !clipMetadataRef.current[activeFilter]) {
46986
- fetchClipMetadata(activeFilter, 1);
48028
+ const category = categoriesForTree.find((cat) => cat.id === activeFilter);
48029
+ if (category) {
48030
+ if (isInitialTimeFilterCategory(activeFilter) && !isCategoryExternallyManaged(activeFilter) && !hasCompleteMetadataForInitialTimeFilter(activeFilter)) {
48031
+ fetchClipMetadata(activeFilter, 1);
48032
+ } else if (!isCategoryExternallyManaged(activeFilter) && !clipMetadataRef.current[activeFilter]) {
48033
+ fetchClipMetadata(activeFilter, 1);
48034
+ }
46987
48035
  }
46988
48036
  }
46989
48037
  }, [activeFilter]);
48038
+ useEffect(() => {
48039
+ const requestedCategory = activeFilter;
48040
+ if (!requestedCategory || !isInitialTimeFilterCategory(requestedCategory) || isCategoryExternallyManaged(requestedCategory) || hasCompleteMetadataForInitialTimeFilter(requestedCategory)) {
48041
+ return;
48042
+ }
48043
+ fetchClipMetadata(requestedCategory, 1);
48044
+ }, [
48045
+ activeFilter,
48046
+ fetchClipMetadata,
48047
+ hasCompleteMetadataForInitialTimeFilter,
48048
+ isCategoryExternallyManaged,
48049
+ isInitialTimeFilterCategory
48050
+ ]);
46990
48051
  useEffect(() => {
46991
48052
  const handleEscape = (e) => {
46992
48053
  if (e.key === "Escape") {
@@ -47050,25 +48111,34 @@ var FileManagerFilters = ({
47050
48111
  }
47051
48112
  try {
47052
48113
  const clipDate = new Date(clipTimestamp);
47053
- const clipTimeStr = clipDate.toLocaleTimeString("en-US", {
48114
+ const clipParts = new Intl.DateTimeFormat("en-US", {
47054
48115
  hour12: false,
47055
48116
  hour: "2-digit",
47056
48117
  minute: "2-digit",
47057
- timeZone: timezone
47058
- });
47059
- return clipTimeStr >= startTime && clipTimeStr <= endTime;
48118
+ timeZone: activeTimeFilterTimezone
48119
+ }).formatToParts(clipDate);
48120
+ const hourValue = clipParts.find((part) => part.type === "hour")?.value;
48121
+ const minuteValue = clipParts.find((part) => part.type === "minute")?.value;
48122
+ const clipMinute = timeValueToMinutes(`${hourValue}:${minuteValue}`);
48123
+ return clipMinute === null ? false : isMinuteInTimeWindow(clipMinute, startTime, endTime);
47060
48124
  } catch (error) {
47061
48125
  console.error("[FileManager] Error parsing clip timestamp:", error);
47062
48126
  return true;
47063
48127
  }
47064
- }, [isTimeFilterActive, startTime, endTime, timezone]);
48128
+ }, [isTimeFilterActive, startTime, endTime, activeTimeFilterTimezone]);
47065
48129
  const filterTree = useMemo(() => {
47066
48130
  const tree = [];
47067
48131
  const regularCategoryNodes = [];
47068
- categories.forEach((category) => {
48132
+ categoriesForTree.forEach((category) => {
48133
+ if (category.id === "fast-cycles" || category.id === "slow-cycles") {
48134
+ return;
48135
+ }
47069
48136
  const categoryCount = counts?.[category.id] || 0;
47070
- const categoryClips = clipMetadata[category.id] || [];
47071
- let filteredClips = categoryClips.filter((clip) => isClipInTimeRange(clip.clip_timestamp));
48137
+ const categoryMetadataClips = getAvailableClipMetadata(category.id);
48138
+ const categoryVideoFallbackClips = categoryMetadataClips.length === 0 && isInitialTimeFilterCategory(category.id) ? videos.filter((video) => video.type === category.id).map(buildClipMetadataFromVideo).filter((clip) => isClipInTimeRange(clip.clip_timestamp)) : [];
48139
+ const categoryClips = categoryMetadataClips.length > 0 ? categoryMetadataClips : categoryVideoFallbackClips;
48140
+ const timeFilteredClips = categoryClips.filter((clip) => isClipInTimeRange(clip.clip_timestamp));
48141
+ let filteredClips = timeFilteredClips;
47072
48142
  if (category.id === RECENT_FLOW_RED_STREAK_CLIP_TYPE2) {
47073
48143
  filteredClips = sortRedFlowMetadata(filteredClips);
47074
48144
  }
@@ -47078,9 +48148,22 @@ var FileManagerFilters = ({
47078
48148
  return classification?.label === idleLabelFilter;
47079
48149
  });
47080
48150
  }
47081
- const displayCount = isTimeFilterActive || category.id === "idle_time" && idleLabelFilter ? filteredClips.length : categoryCount;
48151
+ const scopedTotal = typeof scopedCategoryTotals[category.id] === "number" ? scopedCategoryTotals[category.id] : null;
48152
+ const isScopedByTimeFilter = isCategoryScopedByTimeFilter(category.id);
48153
+ const scopedPageLoaded = isScopedByTimeFilter && typeof scopedCategoryTotals[category.id] === "number";
48154
+ const scopedResponseHadOutOfHourClips = categoryClips.length > filteredClips.length;
48155
+ const shouldTrustScopedTotal = !scopedResponseHadOutOfHourClips || Boolean(categoryHasMore[category.id]);
48156
+ const isCategoryMetadataLoading = Array.from(loadingCategories).some((key) => key.startsWith(`${category.id}-`));
48157
+ const isScopedTotalPending = isTimeFilterActive && isScopedByTimeFilter && scopedTotal === null;
48158
+ const displayCount = isScopedTotalPending ? null : isTimeFilterActive && isScopedByTimeFilter && scopedTotal !== null ? shouldTrustScopedTotal ? scopedTotal : filteredClips.length : isTimeFilterActive || category.id === "idle_time" && idleLabelFilter ? filteredClips.length : categoryCount;
48159
+ const shouldShowScopedEmptyCategory = Boolean(
48160
+ scopedPageLoaded && !isCategoryMetadataLoading && !categoryHasMore[category.id] && filteredClips.length === 0 && (isRequiredHourlyFilterCategory(category.id) || activeFilter === category.id)
48161
+ );
48162
+ const shouldShowScopedLoadingCategory = Boolean(
48163
+ isScopedTotalPending && (isRequiredHourlyFilterCategory(category.id) || !activeInitialTimeFilter || activeFilter === category.id)
48164
+ );
47082
48165
  const shouldShowEmptyIdleTime = category.id === "idle_time" && idleLabelFilter;
47083
- if ((displayCount > 0 || shouldShowEmptyIdleTime) && shouldShowCategory(category.id)) {
48166
+ if ((typeof displayCount === "number" && displayCount > 0 || shouldShowEmptyIdleTime || isCategoryMetadataLoading || shouldShowScopedEmptyCategory || shouldShowScopedLoadingCategory) && shouldShowCategory(category.id)) {
47084
48167
  const colorClasses = getColorClasses(category.color);
47085
48168
  const clipNodes = filteredClips.map((clip, index) => {
47086
48169
  const cycleTime = extractCycleTimeSeconds(clip);
@@ -47090,7 +48173,7 @@ var FileManagerFilters = ({
47090
48173
  const baseTimeLabel = formatClipExplorerTimeLabel({
47091
48174
  categoryId: category.id,
47092
48175
  clipTimestamp: clip.clip_timestamp,
47093
- timezone,
48176
+ timezone: activeTimeFilterTimezone,
47094
48177
  durationSeconds: idleDuration ?? clip.duration,
47095
48178
  idleStartTime: clip.idle_start_time,
47096
48179
  idleEndTime: clip.idle_end_time,
@@ -47128,6 +48211,7 @@ var FileManagerFilters = ({
47128
48211
  type: "category",
47129
48212
  count: displayCount,
47130
48213
  // Use filtered count when time filter is active
48214
+ countLoading: isScopedTotalPending || isCategoryMetadataLoading && scopedTotal === null,
47131
48215
  children: clipNodes,
47132
48216
  // Use clip nodes from metadata
47133
48217
  icon: expandedNodes.has(category.id) ? /* @__PURE__ */ jsx(FolderOpen, { className: `h-4 w-4 ${colorClasses.text}` }) : getCategoryIcon(category.icon, colorClasses),
@@ -47135,10 +48219,18 @@ var FileManagerFilters = ({
47135
48219
  });
47136
48220
  }
47137
48221
  });
47138
- const filteredFastCycles = (percentileClips["fast-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""));
47139
- const filteredSlowCycles = (percentileClips["slow-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""));
48222
+ const filteredFastCycles = sortPercentileCycleClipsForDisplay(
48223
+ "fast-cycles",
48224
+ (percentileClips["fast-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""))
48225
+ );
48226
+ const filteredSlowCycles = sortPercentileCycleClipsForDisplay(
48227
+ "slow-cycles",
48228
+ (percentileClips["slow-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""))
48229
+ );
47140
48230
  const fastCount = typeof percentileCounts["fast-cycles"] === "number" ? percentileCounts["fast-cycles"] : null;
47141
48231
  const slowCount = typeof percentileCounts["slow-cycles"] === "number" ? percentileCounts["slow-cycles"] : null;
48232
+ const isFastCountPending = showPercentileCycleFilters && fastCount === null;
48233
+ const isSlowCountPending = showPercentileCycleFilters && slowCount === null;
47142
48234
  const percentileCategories = showPercentileCycleFilters ? [
47143
48235
  {
47144
48236
  id: "fast-cycles",
@@ -47147,6 +48239,7 @@ var FileManagerFilters = ({
47147
48239
  description: "Top 10% fastest performance",
47148
48240
  type: "percentile-category",
47149
48241
  count: isTimeFilterActive ? fastCount === null && filteredFastCycles.length === 0 ? null : filteredFastCycles.length : fastCount,
48242
+ countLoading: isFastCountPending,
47150
48243
  icon: getPercentileIcon("fast-cycles", expandedNodes.has("fast-cycles"), { text: "text-green-600" }),
47151
48244
  color: "green",
47152
48245
  percentileType: "fast-cycles",
@@ -47158,7 +48251,7 @@ var FileManagerFilters = ({
47158
48251
  label: `${formatClipExplorerTimeLabel({
47159
48252
  categoryId: "fast-cycles",
47160
48253
  clipTimestamp: clip.creation_timestamp || clip.timestamp || "",
47161
- timezone,
48254
+ timezone: activeTimeFilterTimezone,
47162
48255
  durationSeconds: cycleTime
47163
48256
  })}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
47164
48257
  type: "video",
@@ -47178,6 +48271,7 @@ var FileManagerFilters = ({
47178
48271
  description: "Bottom 10% slowest performance",
47179
48272
  type: "percentile-category",
47180
48273
  count: isTimeFilterActive ? slowCount === null && filteredSlowCycles.length === 0 ? null : filteredSlowCycles.length : slowCount,
48274
+ countLoading: isSlowCountPending,
47181
48275
  icon: getPercentileIcon("slow-cycles", expandedNodes.has("slow-cycles"), { text: "text-red-600" }),
47182
48276
  color: "red",
47183
48277
  percentileType: "slow-cycles",
@@ -47189,7 +48283,7 @@ var FileManagerFilters = ({
47189
48283
  label: `${formatClipExplorerTimeLabel({
47190
48284
  categoryId: "slow-cycles",
47191
48285
  clipTimestamp: clip.creation_timestamp || clip.timestamp || "",
47192
- timezone,
48286
+ timezone: activeTimeFilterTimezone,
47193
48287
  durationSeconds: cycleTime
47194
48288
  })}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
47195
48289
  type: "video",
@@ -47220,7 +48314,7 @@ var FileManagerFilters = ({
47220
48314
  hour12: true,
47221
48315
  hour: 'numeric',
47222
48316
  minute: '2-digit',
47223
- timeZone: timezone
48317
+ timeZone: activeTimeFilterTimezone
47224
48318
  });
47225
48319
 
47226
48320
  return {
@@ -47239,7 +48333,11 @@ var FileManagerFilters = ({
47239
48333
  const orderedIds = [RECENT_FLOW_RED_STREAK_CLIP_TYPE2, "cycle_completion", "fast-cycles", "slow-cycles", "idle_time"];
47240
48334
  orderedIds.forEach((orderedId) => {
47241
48335
  const percentileCategory = percentileCategories.find((cat) => cat.id === orderedId);
47242
- const shouldIncludePercentile = percentileCategory ? typeof percentileCategory.count === "number" && percentileCategory.count > 0 : false;
48336
+ const shouldIncludePercentile = percentileCategory ? typeof percentileCategory.count === "number" || Boolean(
48337
+ isTimeFilterActive && isRequiredHourlyFilterCategory(orderedId) && typeof percentileCategory.count === "number" && percentileCategory.count === 0
48338
+ ) || Boolean(
48339
+ percentileCategory.countLoading && (isRequiredHourlyFilterCategory(orderedId) || !activeInitialTimeFilter || activeFilter === orderedId)
48340
+ ) : false;
47243
48341
  if (percentileCategory && shouldIncludePercentile && shouldShowCategory(orderedId)) {
47244
48342
  tree.push(percentileCategory);
47245
48343
  }
@@ -47254,23 +48352,263 @@ var FileManagerFilters = ({
47254
48352
  }
47255
48353
  });
47256
48354
  percentileCategories.forEach((category) => {
47257
- const shouldIncludePercentile = typeof category.count === "number" && category.count > 0;
48355
+ const shouldIncludePercentile = typeof category.count === "number" || Boolean(
48356
+ isTimeFilterActive && isRequiredHourlyFilterCategory(category.id) && typeof category.count === "number" && category.count === 0
48357
+ ) || Boolean(
48358
+ category.countLoading && (isRequiredHourlyFilterCategory(category.id) || !activeInitialTimeFilter || activeFilter === category.id)
48359
+ );
47258
48360
  if (!orderedIds.includes(category.id) && shouldIncludePercentile && shouldShowCategory(category.id)) {
47259
48361
  tree.push(category);
47260
48362
  }
47261
48363
  });
47262
48364
  return tree;
47263
- }, [categories, expandedNodes, counts, clipMetadata, percentileCounts, percentileClips, shouldShowCategory, getPercentileIcon, isClipInTimeRange, isTimeFilterActive, showPercentileCycleFilters]);
48365
+ }, [categoriesForTree, expandedNodes, counts, getAvailableClipMetadata, percentileCounts, percentileClips, shouldShowCategory, getPercentileIcon, isClipInTimeRange, isTimeFilterActive, showPercentileCycleFilters, loadingCategories, activeTimeFilterTimezone, isInitialTimeFilterCategory, isRequiredHourlyFilterCategory, hasCompleteMetadataForInitialTimeFilter, scopedCategoryTotals, isCategoryScopedByTimeFilter, categoryHasMore, activeFilter]);
48366
+ const chartHandoffVideoFallbackTree = useMemo(() => {
48367
+ if (!startTime || !endTime || !activeInitialTimeFilter || !activeFilter) {
48368
+ return [];
48369
+ }
48370
+ const fallbackCategory = categoriesForTree.find((category) => category.id === activeFilter) || categoriesForTree.find((category) => videos.some((video) => video.type === category.id));
48371
+ if (!fallbackCategory) {
48372
+ return [];
48373
+ }
48374
+ const fallbackVideos = videos.filter((video) => video.type === fallbackCategory.id);
48375
+ const sourceVideos = fallbackVideos.map(buildClipMetadataFromVideo).filter((clip) => isClipInTimeRange(clip.clip_timestamp));
48376
+ if (sourceVideos.length === 0) {
48377
+ return [];
48378
+ }
48379
+ const colorClasses = getColorClasses(fallbackCategory.color);
48380
+ const clipNodes = sourceVideos.map((clip, index) => {
48381
+ const cycleTime = extractCycleTimeSeconds(clip);
48382
+ const baseTimeLabel = formatClipExplorerTimeLabel({
48383
+ categoryId: fallbackCategory.id,
48384
+ clipTimestamp: clip.clip_timestamp,
48385
+ timezone: activeTimeFilterTimezone,
48386
+ durationSeconds: clip.duration,
48387
+ idleStartTime: clip.idle_start_time,
48388
+ idleEndTime: clip.idle_end_time,
48389
+ clipStartTime: clip.clip_start_time,
48390
+ clipEndTime: clip.clip_end_time
48391
+ });
48392
+ const displayLabel = `${baseTimeLabel}${clip.duration && fallbackCategory.id !== "idle_time" && fallbackCategory.id !== "low_value" && fallbackCategory.id !== RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? ` - (${clip.duration.toFixed(1)}s)` : ""}`;
48393
+ return {
48394
+ id: clip.id,
48395
+ label: displayLabel,
48396
+ type: "video",
48397
+ icon: getSeverityIcon(clip.severity, fallbackCategory.id, cycleTime, resolvedTargetCycleTime, clip.clipId),
48398
+ timestamp: clip.clip_timestamp,
48399
+ severity: clip.severity,
48400
+ clipId: clip.clipId,
48401
+ categoryId: fallbackCategory.id,
48402
+ clipPosition: index + 1,
48403
+ cycleTimeSeconds: cycleTime,
48404
+ duration: clip.duration,
48405
+ cycleItemCount: null,
48406
+ redFlowSeverityScore: clip.red_flow_severity_score ?? null,
48407
+ redFlowExplanationSummary: clip.red_flow_explanation_summary ?? clip.red_flow_explanation?.summary ?? null
48408
+ };
48409
+ });
48410
+ return [{
48411
+ id: activeFilter,
48412
+ label: fallbackCategory.id === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? RECENT_FLOW_RED_STREAK_DISPLAY_LABEL : fallbackCategory.label,
48413
+ subtitle: fallbackCategory.subtitle || fallbackCategory.description,
48414
+ description: fallbackCategory.description,
48415
+ type: "category",
48416
+ count: clipNodes.length,
48417
+ children: clipNodes,
48418
+ icon: /* @__PURE__ */ jsx(FolderOpen, { className: `h-4 w-4 ${colorClasses.text}` }),
48419
+ color: fallbackCategory.color
48420
+ }];
48421
+ }, [
48422
+ activeFilter,
48423
+ activeInitialTimeFilter,
48424
+ activeTimeFilterTimezone,
48425
+ categoriesForTree,
48426
+ counts,
48427
+ currentVideoId,
48428
+ endTime,
48429
+ getDisplayValue,
48430
+ isClipInTimeRange,
48431
+ resolvedTargetCycleTime,
48432
+ startTime,
48433
+ videos
48434
+ ]);
48435
+ const displayedFilterTree = useMemo(() => {
48436
+ if (chartHandoffVideoFallbackTree.length === 0) {
48437
+ return filterTree;
48438
+ }
48439
+ if (filterTree.length === 0) {
48440
+ return chartHandoffVideoFallbackTree;
48441
+ }
48442
+ const fallbackNode = chartHandoffVideoFallbackTree[0];
48443
+ const activeNode = filterTree.find((node) => node.id === activeFilter);
48444
+ const activeNodeHasChildren = (activeNode?.children?.length || 0) > 0;
48445
+ const shouldReplaceActiveNode = Boolean(
48446
+ activeFilter && fallbackNode && activeNode && !activeNodeHasChildren && isInitialTimeFilterCategory(activeFilter)
48447
+ );
48448
+ if (!shouldReplaceActiveNode) {
48449
+ return filterTree;
48450
+ }
48451
+ return filterTree.map((node) => node.id === activeFilter ? fallbackNode : node);
48452
+ }, [activeFilter, chartHandoffVideoFallbackTree, filterTree, isInitialTimeFilterCategory]);
48453
+ const getSelectionContextForNodes = useCallback((categoryId, clipNodes) => {
48454
+ const normalizeSeverity = (severity) => severity === "low" || severity === "medium" || severity === "high" ? severity : "medium";
48455
+ const scopedNodes = clipNodes.filter((node) => node.type === "video" && Boolean(node.clipId || node.id));
48456
+ const scopedNodeClips = scopedNodes.map((node, index) => ({
48457
+ id: node.clipId || node.id,
48458
+ clipId: node.clipId || node.id,
48459
+ clip_timestamp: node.timestamp || "",
48460
+ creation_timestamp: node.timestamp || "",
48461
+ description: node.label,
48462
+ severity: normalizeSeverity(node.severity),
48463
+ category: categoryId,
48464
+ duration: node.duration,
48465
+ cycle_time_seconds: node.cycleTimeSeconds,
48466
+ index
48467
+ }));
48468
+ const categoryClips = getAvailableClipMetadata(categoryId);
48469
+ const contextClips = isCategoryScopedByTimeFilter(categoryId) && scopedNodeClips.length > 0 ? scopedNodeClips : categoryClips.length > 0 ? categoryClips : scopedNodeClips;
48470
+ const scopedTotal = scopedCategoryTotals[categoryId];
48471
+ const total = typeof scopedTotal === "number" ? scopedTotal : counts?.[categoryId];
48472
+ return contextClips.length ? { clips: contextClips, total } : void 0;
48473
+ }, [counts, getAvailableClipMetadata, isCategoryScopedByTimeFilter, scopedCategoryTotals]);
48474
+ useEffect(() => {
48475
+ if (!activeInitialTimeFilter || !startTime || !endTime || currentVideoId || !onClipSelect) {
48476
+ return;
48477
+ }
48478
+ const categoryPriority = Array.from(new Set([
48479
+ activeInitialTimeFilter.categoryId,
48480
+ activeFilter,
48481
+ ...activeInitialTimeFilter.categoryIds || []
48482
+ ].filter((value) => typeof value === "string" && value.length > 0)));
48483
+ const preferredCategoryId = categoryPriority[0];
48484
+ const orderedCategoryNodes = displayedFilterTree.filter((categoryNode) => isInitialTimeFilterCategory(categoryNode.id)).sort((left, right) => {
48485
+ const leftIndex = categoryPriority.indexOf(left.id);
48486
+ const rightIndex = categoryPriority.indexOf(right.id);
48487
+ const leftPriority = leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex;
48488
+ const rightPriority = rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex;
48489
+ return leftPriority - rightPriority;
48490
+ });
48491
+ const preferredCategoryNode = preferredCategoryId ? orderedCategoryNodes.find((categoryNode) => categoryNode.id === preferredCategoryId) : void 0;
48492
+ if (preferredCategoryId && !preferredCategoryNode) {
48493
+ const preferredKnownTotal = preferredCategoryId === "fast-cycles" || preferredCategoryId === "slow-cycles" ? percentileCounts[preferredCategoryId] : scopedCategoryTotals[preferredCategoryId];
48494
+ if (preferredKnownTotal !== 0) {
48495
+ return;
48496
+ }
48497
+ }
48498
+ const preferredFirstClipNode = preferredCategoryNode?.children?.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
48499
+ if (preferredCategoryId && preferredCategoryNode && !preferredFirstClipNode) {
48500
+ const preferredKnownTotal = preferredCategoryId === "fast-cycles" || preferredCategoryId === "slow-cycles" ? percentileCounts[preferredCategoryId] : scopedCategoryTotals[preferredCategoryId];
48501
+ if (preferredKnownTotal !== 0) {
48502
+ return;
48503
+ }
48504
+ }
48505
+ for (const categoryNode of orderedCategoryNodes) {
48506
+ const firstClipNode = categoryNode.children?.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
48507
+ if (!firstClipNode?.clipId || !firstClipNode.categoryId) {
48508
+ continue;
48509
+ }
48510
+ const selectionKey = `${activeTimeFilterKey}:${firstClipNode.categoryId}:${firstClipNode.clipId}:${currentVideoId || "none"}`;
48511
+ if (autoSelectedScopedClipRef.current === selectionKey) {
48512
+ return;
48513
+ }
48514
+ autoSelectedScopedClipRef.current = selectionKey;
48515
+ const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, categoryNode.children || []);
48516
+ onClipSelect(
48517
+ firstClipNode.categoryId,
48518
+ firstClipNode.clipId,
48519
+ firstClipNode.clipPosition,
48520
+ selectionContext
48521
+ );
48522
+ return;
48523
+ }
48524
+ }, [
48525
+ activeInitialTimeFilter,
48526
+ activeFilter,
48527
+ activeTimeFilterKey,
48528
+ currentVideoId,
48529
+ displayedFilterTree,
48530
+ endTime,
48531
+ getSelectionContextForNodes,
48532
+ isInitialTimeFilterCategory,
48533
+ onClipSelect,
48534
+ percentileCounts,
48535
+ scopedCategoryTotals,
48536
+ startTime
48537
+ ]);
48538
+ const isChartHandoffLoading = useMemo(() => {
48539
+ if (!startTime || !endTime || !activeInitialTimeFilter || !activeFilter || !isInitialTimeFilterCategory(activeFilter)) {
48540
+ return false;
48541
+ }
48542
+ const pageOneLoadingKey = getMetadataLoadingKey(activeFilter, 1);
48543
+ return Boolean(
48544
+ activeCategoryLoading || Array.from(loadingCategories).some((key) => key === pageOneLoadingKey || key.startsWith(`${pageOneLoadingKey}-all`))
48545
+ );
48546
+ }, [
48547
+ activeCategoryLoading,
48548
+ activeFilter,
48549
+ activeInitialTimeFilter,
48550
+ endTime,
48551
+ getMetadataLoadingKey,
48552
+ isInitialTimeFilterCategory,
48553
+ loadingCategories,
48554
+ startTime
48555
+ ]);
48556
+ useEffect(() => {
48557
+ if (!isTimeFilterActive || !startTime || !endTime || !activeFilter || activeFilter === "all" || !onClipSelect) {
48558
+ return;
48559
+ }
48560
+ const activeCategoryNode = displayedFilterTree.find((categoryNode) => categoryNode.id === activeFilter);
48561
+ const activeClipNodes = (activeCategoryNode?.children || []).filter((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
48562
+ if (activeClipNodes.length === 0) {
48563
+ return;
48564
+ }
48565
+ const currentClipIsVisible = Boolean(
48566
+ currentVideoId && activeClipNodes.some((child) => (child.clipId || child.id) === currentVideoId)
48567
+ );
48568
+ if (currentClipIsVisible) {
48569
+ return;
48570
+ }
48571
+ const firstClipNode = activeClipNodes[0];
48572
+ if (!firstClipNode?.clipId || !firstClipNode.categoryId) {
48573
+ return;
48574
+ }
48575
+ const selectionKey = `${activeTimeFilterKey}:${firstClipNode.categoryId}:${firstClipNode.clipId}:${currentVideoId || "none"}`;
48576
+ if (autoSelectedScopedClipRef.current === selectionKey) {
48577
+ return;
48578
+ }
48579
+ autoSelectedScopedClipRef.current = selectionKey;
48580
+ const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, activeClipNodes);
48581
+ onClipSelect(
48582
+ firstClipNode.categoryId,
48583
+ firstClipNode.clipId,
48584
+ firstClipNode.clipPosition,
48585
+ selectionContext
48586
+ );
48587
+ }, [
48588
+ activeFilter,
48589
+ activeTimeFilterKey,
48590
+ currentVideoId,
48591
+ displayedFilterTree,
48592
+ endTime,
48593
+ getSelectionContextForNodes,
48594
+ isTimeFilterActive,
48595
+ onClipSelect,
48596
+ startTime
48597
+ ]);
47264
48598
  const toggleExpanded = (nodeId) => {
47265
48599
  const newExpanded = new Set(expandedNodes);
47266
48600
  if (newExpanded.has(nodeId)) {
47267
48601
  newExpanded.delete(nodeId);
47268
48602
  } else {
47269
48603
  newExpanded.add(nodeId);
47270
- const category = categories.find((cat) => cat.id === nodeId);
47271
- if (category && !clipMetadata[nodeId] && !isCategoryExternallyManaged(nodeId)) {
48604
+ const category = categoriesForTree.find((cat) => cat.id === nodeId);
48605
+ if (category) {
47272
48606
  console.log(`[FileManager] Fetching clips for expanded category: ${nodeId}`);
47273
- fetchClipMetadata(nodeId, 1);
48607
+ if (isInitialTimeFilterCategory(nodeId) && !hasCompleteMetadataForInitialTimeFilter(nodeId)) {
48608
+ fetchClipMetadata(nodeId, 1);
48609
+ } else if (!hasKnownClipMetadata(nodeId) && !isCategoryExternallyManaged(nodeId)) {
48610
+ fetchClipMetadata(nodeId, 1);
48611
+ }
47274
48612
  }
47275
48613
  if (!isCategoryExternallyManaged(nodeId) && showPercentileCycleFilters && nodeId === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
47276
48614
  fetchPercentileClips("fast-cycles");
@@ -47288,10 +48626,14 @@ var FileManagerFilters = ({
47288
48626
  newExpanded.delete(node.id);
47289
48627
  } else {
47290
48628
  newExpanded.add(node.id);
47291
- const category = categories.find((cat) => cat.id === node.id);
47292
- if (category && !clipMetadata[node.id] && !isCategoryExternallyManaged(node.id)) {
48629
+ const category = categoriesForTree.find((cat) => cat.id === node.id);
48630
+ if (category) {
47293
48631
  console.log(`[FileManager] Fetching clips for expanded category: ${node.id}`);
47294
- fetchClipMetadata(node.id, 1);
48632
+ if (isInitialTimeFilterCategory(node.id) && !hasCompleteMetadataForInitialTimeFilter(node.id)) {
48633
+ fetchClipMetadata(node.id, 1);
48634
+ } else if (!hasKnownClipMetadata(node.id) && !isCategoryExternallyManaged(node.id)) {
48635
+ fetchClipMetadata(node.id, 1);
48636
+ }
47295
48637
  }
47296
48638
  if (!isCategoryExternallyManaged(node.id) && showPercentileCycleFilters && node.id === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
47297
48639
  fetchPercentileClips("fast-cycles");
@@ -47301,7 +48643,21 @@ var FileManagerFilters = ({
47301
48643
  }
47302
48644
  }
47303
48645
  setExpandedNodes(newExpanded);
47304
- onFilterChange(node.id);
48646
+ if (node.id !== activeFilter) {
48647
+ onFilterChange(node.id);
48648
+ }
48649
+ if (isCategoryScopedByTimeFilter(node.id) && onClipSelect && node.children?.length) {
48650
+ const firstClipNode = node.children.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
48651
+ if (firstClipNode?.clipId && firstClipNode.categoryId) {
48652
+ const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, node.children);
48653
+ onClipSelect(
48654
+ firstClipNode.categoryId,
48655
+ firstClipNode.clipId,
48656
+ firstClipNode.clipPosition,
48657
+ selectionContext
48658
+ );
48659
+ }
48660
+ }
47305
48661
  if (node.id === "fast-cycles") {
47306
48662
  trackCoreEvent("Fast Clips Clicked", {
47307
48663
  workspaceId,
@@ -47339,8 +48695,11 @@ var FileManagerFilters = ({
47339
48695
  } else if (node.type === "video") {
47340
48696
  if (onClipSelect && node.categoryId !== void 0 && node.clipId !== void 0) {
47341
48697
  console.log(`[FileManager] Selecting clip: category=${node.categoryId}, clipId=${node.clipId}, position=${node.clipPosition}`);
47342
- const categoryClips = node.categoryId ? clipMetadata[node.categoryId] : void 0;
47343
- const selectionContext = categoryClips?.length ? { clips: categoryClips, total: counts?.[node.categoryId] } : void 0;
48698
+ const categoryNode = displayedFilterTree.find((candidate) => candidate.id === node.categoryId);
48699
+ const selectionContext = getSelectionContextForNodes(
48700
+ node.categoryId,
48701
+ categoryNode?.children || [node]
48702
+ );
47344
48703
  onClipSelect(node.categoryId, node.clipId, node.clipPosition, selectionContext);
47345
48704
  } else {
47346
48705
  const videoIndex = videos.findIndex((v) => v.id === node.id);
@@ -47353,24 +48712,40 @@ var FileManagerFilters = ({
47353
48712
  const renderNode = (node, depth = 0) => {
47354
48713
  const isExpanded = expandedNodes.has(node.id);
47355
48714
  const isActive = activeFilter === node.id;
47356
- const isCurrentVideo = currentVideoId === node.id;
47357
- const isCountUnknown = node.type === "percentile-category" && node.count === null;
47358
- const hasChildren = isCountUnknown || (node.count || 0) > 0;
47359
- const hasLoadedChildren = (clipMetadata[node.id]?.length || percentileClips[node.id]?.length || 0) > 0;
48715
+ const nodeClipId = node.clipId || node.id;
48716
+ const isCurrentVideo = Boolean(
48717
+ node.type === "video" && currentVideoId && currentVideoId === nodeClipId
48718
+ );
48719
+ const isCountUnknown = Boolean(
48720
+ (node.type === "category" || node.type === "percentile-category") && (node.count === null || node.countLoading)
48721
+ );
48722
+ const hasRenderedChildren = (node.children?.length || 0) > 0;
47360
48723
  const loadedPage = categoryPages[node.id] || 0;
47361
48724
  const pageOneLoadingKey = getMetadataLoadingKey(node.id, 1);
47362
48725
  const nextMetadataPage = loadedPage + 1;
47363
48726
  const nextLoadMorePage = (categoryPages[node.id] || 1) + 1;
47364
48727
  const isPageOneLoading = loadingCategories.has(pageOneLoadingKey);
48728
+ const isFullCategoryLoading = Array.from(loadingCategories).some((key) => key.startsWith(`${pageOneLoadingKey}-all`));
47365
48729
  const isLoadMoreLoading = loadedPage > 0 && loadingCategories.has(getMetadataLoadingKey(node.id, nextMetadataPage));
48730
+ const isNodeLoading = Boolean(
48731
+ isPageOneLoading || isFullCategoryLoading || activeCategoryLoading && node.id === activeFilter
48732
+ );
48733
+ const totalForLoadMore = isCategoryScopedByTimeFilter(node.id) && typeof scopedCategoryTotals[node.id] === "number" ? scopedCategoryTotals[node.id] : counts?.[node.id] || 0;
47366
48734
  const showInitialLoadingState = Boolean(
47367
- isExpanded && !hasLoadedChildren && (node.type === "category" || node.type === "percentile-category") && (isPageOneLoading || activeCategoryLoading && node.id === activeFilter)
48735
+ isExpanded && !hasRenderedChildren && (node.type === "category" || node.type === "percentile-category") && isNodeLoading
48736
+ );
48737
+ const showScopedEmptyState = Boolean(
48738
+ isExpanded && !hasRenderedChildren && !isNodeLoading && !categoryHasMore[node.id] && isCategoryScopedByTimeFilter(node.id) && (node.type === "category" || node.type === "percentile-category")
47368
48739
  );
48740
+ const hasChildren = isCountUnknown || (node.count || 0) > 0 || isNodeLoading || showScopedEmptyState;
47369
48741
  const colorClasses = node.color ? getColorClasses(node.color) : null;
47370
48742
  return /* @__PURE__ */ jsxs("div", { className: "select-none animate-in", children: [
47371
48743
  /* @__PURE__ */ jsxs(
47372
48744
  "div",
47373
48745
  {
48746
+ "aria-current": isCurrentVideo ? "true" : void 0,
48747
+ "data-qa-clips-row-clip-id": node.type === "video" ? nodeClipId : void 0,
48748
+ "data-qa-clips-row-category-id": node.type === "video" ? node.categoryId : void 0,
47374
48749
  className: `flex items-center cursor-pointer transition-all duration-300 ease-out group relative overflow-hidden ${node.type === "category" && depth === 0 ? `py-3 px-4 rounded-2xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-blue-50/30 hover:shadow-lg hover:shadow-blue-100/20 hover:scale-[1.02] hover:-translate-y-0.5 ${isActive ? "bg-gradient-to-r from-blue-50 via-blue-50/80 to-indigo-50/60 border border-blue-200/50 shadow-lg shadow-blue-100/30 scale-[1.02]" : "border border-transparent"}` : `py-2 px-3 rounded-xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-slate-50/50 hover:shadow-sm ${isActive ? "bg-gradient-to-r from-blue-50/80 to-blue-50/40 border border-blue-200/30 shadow-sm" : "border border-transparent"} ${isCurrentVideo ? "bg-gradient-to-r from-blue-50 to-blue-50/60 border border-blue-200/50 shadow-md shadow-blue-100/20" : ""}`} ${node.type === "video" ? "ml-6" : ""}`,
47375
48750
  onClick: () => handleNodeClick(node),
47376
48751
  children: [
@@ -47406,7 +48781,7 @@ var FileManagerFilters = ({
47406
48781
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between", children: [
47407
48782
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
47408
48783
  /* @__PURE__ */ jsx("div", { className: `font-semibold tracking-tight ${node.type === "category" || node.type === "percentile-category" ? "text-slate-800 text-sm" : "text-slate-700 text-xs"} ${isCurrentVideo ? "text-blue-700 font-bold" : ""} group-hover:text-slate-900 transition-colors duration-200`, children: node.label }),
47409
- node.type === "category" && (node.subtitle || categories.find((c) => c.id === node.id)?.description) && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle || categories.find((c) => c.id === node.id)?.description }),
48784
+ node.type === "category" && (node.subtitle || categoriesForTree.find((c) => c.id === node.id)?.description) && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle || categoriesForTree.find((c) => c.id === node.id)?.description }),
47410
48785
  node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
47411
48786
  node.type === "video" && (node.severity || node.categoryId === "cycle_completion" || node.categoryId === "idle_time" || node.categoryId === "low_value" || node.categoryId === RECENT_FLOW_RED_STREAK_CLIP_TYPE2) && /* @__PURE__ */ jsx("div", { className: "text-xs mt-0.5 font-medium", children: node.categoryId === "idle_time" ? (
47412
48787
  // Show root cause label for idle time clips (text only, icon is on the left)
@@ -47442,7 +48817,7 @@ var FileManagerFilters = ({
47442
48817
  })()
47443
48818
  ) })
47444
48819
  ] }),
47445
- node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsx("span", { className: `px-2.5 py-1 text-sm font-bold rounded-lg shadow-sm border backdrop-blur-sm flex-shrink-0 group-hover:scale-105 transition-all duration-200 ${colorClasses ? `${colorClasses.bg} ${colorClasses.text} ${colorClasses.border} bg-opacity-80` : "bg-slate-100/80 text-slate-700 border-slate-200/60"}`, children: node.count === null ? "\u2014" : node.count }) })
48820
+ node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsx("span", { className: `px-2.5 py-1 text-sm font-bold rounded-lg shadow-sm border backdrop-blur-sm flex-shrink-0 group-hover:scale-105 transition-all duration-200 ${colorClasses ? `${colorClasses.bg} ${colorClasses.text} ${colorClasses.border} bg-opacity-80` : "bg-slate-100/80 text-slate-700 border-slate-200/60"}`, children: isCountUnknown ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : node.count }) })
47446
48821
  ] })
47447
48822
  ]
47448
48823
  }
@@ -47453,6 +48828,7 @@ var FileManagerFilters = ({
47453
48828
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
47454
48829
  "Loading clips..."
47455
48830
  ] }) }),
48831
+ showScopedEmptyState && /* @__PURE__ */ jsx("div", { className: "py-2 px-3 text-center text-sm text-slate-500", children: "No clips found" }),
47456
48832
  isLoadMoreLoading && /* @__PURE__ */ jsx("div", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center text-sm text-slate-500", children: [
47457
48833
  /* @__PURE__ */ jsx("div", { className: "animate-spin mr-2 h-4 w-4 border-2 border-slate-300 border-t-blue-500 rounded-full" }),
47458
48834
  "Loading more clips..."
@@ -47467,7 +48843,7 @@ var FileManagerFilters = ({
47467
48843
  className: "w-full py-2 px-3 text-sm text-blue-600 hover:bg-blue-50 rounded-lg transition-colors duration-200 text-center",
47468
48844
  children: [
47469
48845
  "Load more clips (",
47470
- (counts?.[node.id] || 0) - (clipMetadata[node.id]?.length || 0),
48846
+ Math.max(0, totalForLoadMore - getAvailableClipMetadata(node.id).length),
47471
48847
  " remaining)"
47472
48848
  ]
47473
48849
  }
@@ -47530,6 +48906,8 @@ var FileManagerFilters = ({
47530
48906
  e.stopPropagation();
47531
48907
  setStartTime("");
47532
48908
  setEndTime("");
48909
+ setActiveInitialTimeFilter(null);
48910
+ setScopedCategoryTotals({});
47533
48911
  setIsTimeFilterActive(false);
47534
48912
  },
47535
48913
  className: "rounded-full p-0.5 transition-colors hover:bg-blue-100",
@@ -47663,6 +49041,8 @@ var FileManagerFilters = ({
47663
49041
  onClick: () => {
47664
49042
  setStartTime("");
47665
49043
  setEndTime("");
49044
+ setActiveInitialTimeFilter(null);
49045
+ setScopedCategoryTotals({});
47666
49046
  setStartSearchTerm("");
47667
49047
  setEndSearchTerm("");
47668
49048
  setIsTimeFilterActive(false);
@@ -47751,8 +49131,13 @@ var FileManagerFilters = ({
47751
49131
  )
47752
49132
  ] }),
47753
49133
  /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 flex-1 min-h-0 overflow-y-auto scrollbar-thin", children: [
47754
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: filterTree.map((node) => renderNode(node)) }),
47755
- filterTree.length === 0 && (startTime || endTime) && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
49134
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: displayedFilterTree.map((node) => renderNode(node)) }),
49135
+ displayedFilterTree.length === 0 && isChartHandoffLoading && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
49136
+ /* @__PURE__ */ jsx("div", { className: "inline-flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(Loader2, { className: "h-8 w-8 animate-spin text-blue-500" }) }),
49137
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "Loading clips..." }),
49138
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Finding clips from the selected hour" })
49139
+ ] }),
49140
+ displayedFilterTree.length === 0 && !isChartHandoffLoading && (startTime || endTime) && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
47756
49141
  /* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(Clock, { className: "h-12 w-12 mx-auto" }) }),
47757
49142
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips found" }),
47758
49143
  /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500 mb-4", children: "No clips match the selected time range" }),
@@ -47762,6 +49147,8 @@ var FileManagerFilters = ({
47762
49147
  onClick: () => {
47763
49148
  setStartTime("");
47764
49149
  setEndTime("");
49150
+ setActiveInitialTimeFilter(null);
49151
+ setScopedCategoryTotals({});
47765
49152
  setIsTimeFilterActive(false);
47766
49153
  },
47767
49154
  className: "inline-flex items-center px-4 py-2 bg-blue-50 text-blue-600 text-sm font-medium rounded-lg hover:bg-blue-100 transition-colors duration-200",
@@ -47769,12 +49156,12 @@ var FileManagerFilters = ({
47769
49156
  }
47770
49157
  )
47771
49158
  ] }),
47772
- filterTree.length === 0 && !startTime && !endTime && categories.length === 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
49159
+ displayedFilterTree.length === 0 && !startTime && !endTime && categories.length === 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
47773
49160
  /* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(HelpCircle, { className: "h-12 w-12 mx-auto" }) }),
47774
49161
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clip types available" }),
47775
49162
  /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Loading clip categories..." })
47776
49163
  ] }),
47777
- filterTree.length === 0 && !startTime && !endTime && categories.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
49164
+ displayedFilterTree.length === 0 && !startTime && !endTime && categories.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
47778
49165
  /* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(Play, { className: "h-12 w-12 mx-auto" }) }),
47779
49166
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips available" }),
47780
49167
  /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "No clips found for the selected time period" })
@@ -48181,6 +49568,65 @@ function useClipsRealtimeUpdates({
48181
49568
  }
48182
49569
  var LOW_EFFICIENCY_CATEGORY_ID = "recent_flow_red_streak";
48183
49570
  var LOW_EFFICIENCY_AI_SUMMARY_ENABLED = process.env.NEXT_PUBLIC_LOW_EFFICIENCY_AI_SUMMARY_ENABLED === "true";
49571
+ var timeValueToMinutes2 = (value) => {
49572
+ const [hourValue, minuteValue] = value.substring(0, 5).split(":").map(Number);
49573
+ if (!Number.isInteger(hourValue) || !Number.isInteger(minuteValue) || hourValue < 0 || hourValue > 23 || minuteValue < 0 || minuteValue > 59) {
49574
+ return null;
49575
+ }
49576
+ return hourValue * 60 + minuteValue;
49577
+ };
49578
+ var isMinuteInTimeWindow2 = (minute, startValue, endValue) => {
49579
+ const startMinute = timeValueToMinutes2(startValue);
49580
+ const endMinute = timeValueToMinutes2(endValue);
49581
+ if (startMinute === null || endMinute === null) {
49582
+ return true;
49583
+ }
49584
+ return endMinute > startMinute ? minute >= startMinute && minute < endMinute : minute >= startMinute || minute < endMinute;
49585
+ };
49586
+ var CHART_HANDOFF_CATEGORY_FALLBACKS = {
49587
+ cycle_completion: {
49588
+ label: "Cycle Completion",
49589
+ description: "Successfully completed production cycles",
49590
+ color: "blue",
49591
+ icon: "check-circle"
49592
+ },
49593
+ "fast-cycles": {
49594
+ label: "Fast Cycles",
49595
+ description: "Fastest cycle clips",
49596
+ color: "green",
49597
+ icon: "trending-up"
49598
+ },
49599
+ "slow-cycles": {
49600
+ label: "Slow Cycles",
49601
+ description: "Slowest cycle clips",
49602
+ color: "red",
49603
+ icon: "trending-down"
49604
+ },
49605
+ idle_time: {
49606
+ label: "Idle Time",
49607
+ description: "Low value activities and idle moments",
49608
+ color: "amber",
49609
+ icon: "clock"
49610
+ },
49611
+ recent_flow_red_streak: {
49612
+ label: "Low moments",
49613
+ description: "Moments of low efficiency",
49614
+ color: "rose",
49615
+ icon: "alert-triangle"
49616
+ },
49617
+ worst_cycle_time: {
49618
+ label: "Slow Cycles",
49619
+ description: "Slowest cycle clips",
49620
+ color: "red",
49621
+ icon: "trending-down"
49622
+ },
49623
+ best_cycle_time: {
49624
+ label: "Fast Cycles",
49625
+ description: "Fastest cycle clips",
49626
+ color: "green",
49627
+ icon: "trending-up"
49628
+ }
49629
+ };
48184
49630
  var parseFiniteNumber2 = (value) => {
48185
49631
  if (typeof value === "number" && Number.isFinite(value)) {
48186
49632
  return value;
@@ -48228,6 +49674,7 @@ var BottlenecksContent = ({
48228
49674
  ticketId,
48229
49675
  prefetchedPercentileCounts,
48230
49676
  lowMomentsPrefetch,
49677
+ initialTimePrefetch,
48231
49678
  initialTimeFilter
48232
49679
  }) => {
48233
49680
  console.log("\u{1F3AB} [BottlenecksContent] Rendered with ticketId:", ticketId || "NONE", "workspaceId:", workspaceId, "date:", date, "shift:", shift);
@@ -48440,6 +49887,108 @@ var BottlenecksContent = ({
48440
49887
  enabled: isEffectiveShiftReady
48441
49888
  });
48442
49889
  const isLowMomentsCategoryAvailable = useMemo(() => Array.isArray(clipTypes) && clipTypes.some((type) => type?.type === LOW_EFFICIENCY_CATEGORY_ID || type?.id === LOW_EFFICIENCY_CATEGORY_ID), [clipTypes]);
49890
+ const requestedInitialCategoryCandidates = useMemo(() => {
49891
+ const requestedCategories = [
49892
+ ...Array.isArray(initialTimeFilter?.categoryIds) ? initialTimeFilter.categoryIds : [],
49893
+ initialTimeFilter?.categoryId
49894
+ ];
49895
+ return Array.from(
49896
+ new Set(
49897
+ requestedCategories.filter((category) => Boolean(category)).map((category) => category.trim()).filter(Boolean)
49898
+ )
49899
+ );
49900
+ }, [initialTimeFilter?.categoryId, initialTimeFilter?.categoryIds]);
49901
+ const hasInitialTimeHandoff = Boolean(initialTimeFilter?.startTime && initialTimeFilter?.endTime);
49902
+ const initialTimeHandoffCategorySet = useMemo(() => new Set(requestedInitialCategoryCandidates), [requestedInitialCategoryCandidates]);
49903
+ const isInitialTimeHandoffCategory = useCallback((categoryId) => Boolean(
49904
+ hasInitialTimeHandoff && categoryId && (initialTimeHandoffCategorySet.size === 0 || initialTimeHandoffCategorySet.has(categoryId))
49905
+ ), [hasInitialTimeHandoff, initialTimeHandoffCategorySet]);
49906
+ const isTimestampInInitialTimeHandoff = useCallback((timestamp) => {
49907
+ if (!hasInitialTimeHandoff || !initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !timestamp) {
49908
+ return true;
49909
+ }
49910
+ try {
49911
+ const clipDate = new Date(timestamp);
49912
+ if (Number.isNaN(clipDate.getTime())) {
49913
+ return false;
49914
+ }
49915
+ const clipParts = new Intl.DateTimeFormat("en-US", {
49916
+ hour12: false,
49917
+ hour: "2-digit",
49918
+ minute: "2-digit",
49919
+ timeZone: initialTimeFilter.timezone || timezone
49920
+ }).formatToParts(clipDate);
49921
+ const hourValue = clipParts.find((part) => part.type === "hour")?.value;
49922
+ const minuteValue = clipParts.find((part) => part.type === "minute")?.value;
49923
+ const clipMinute = timeValueToMinutes2(`${hourValue}:${minuteValue}`);
49924
+ return clipMinute === null ? false : isMinuteInTimeWindow2(clipMinute, initialTimeFilter.startTime, initialTimeFilter.endTime);
49925
+ } catch {
49926
+ return false;
49927
+ }
49928
+ }, [
49929
+ hasInitialTimeHandoff,
49930
+ initialTimeFilter?.endTime,
49931
+ initialTimeFilter?.startTime,
49932
+ initialTimeFilter?.timezone,
49933
+ timezone
49934
+ ]);
49935
+ const resolvedInitialCategory = useMemo(() => resolveInitialClipCategory(requestedInitialCategoryCandidates, clipTypes, dynamicCounts), [clipTypes, dynamicCounts, requestedInitialCategoryCandidates]);
49936
+ const initialTimeFilterKey = useMemo(() => hasInitialTimeHandoff ? [
49937
+ initialTimeFilter?.startTime,
49938
+ initialTimeFilter?.endTime,
49939
+ initialTimeFilter?.timezone || timezone,
49940
+ initialTimeFilter?.categoryId || "",
49941
+ ...initialTimeFilter?.categoryIds || []
49942
+ ].join("|") : "", [
49943
+ hasInitialTimeHandoff,
49944
+ initialTimeFilter?.categoryId,
49945
+ initialTimeFilter?.categoryIds,
49946
+ initialTimeFilter?.endTime,
49947
+ initialTimeFilter?.startTime,
49948
+ initialTimeFilter?.timezone,
49949
+ timezone
49950
+ ]);
49951
+ const preferredInitialTimeCategory = resolvedInitialCategory || requestedInitialCategoryCandidates[0] || "";
49952
+ useEffect(() => {
49953
+ if (!initialTimeFilterKey) {
49954
+ return;
49955
+ }
49956
+ setPendingVideo(null);
49957
+ setAllVideos([]);
49958
+ setCurrentIndex(0);
49959
+ setCurrentClipId(null);
49960
+ currentClipIdRef.current = null;
49961
+ setCurrentMetadataIndex(0);
49962
+ currentMetadataIndexRef.current = 0;
49963
+ setCurrentPosition(0);
49964
+ currentPositionRef.current = 0;
49965
+ setCurrentTotal(0);
49966
+ currentTotalRef.current = 0;
49967
+ setCategoryMetadata([]);
49968
+ categoryMetadataRef.current = [];
49969
+ setCategoryMetadataCategoryId(null);
49970
+ setCategoryMetadataSort(null);
49971
+ clearRetryTimeout();
49972
+ navigationLockRef.current = false;
49973
+ loadingCategoryRef.current = null;
49974
+ if (loadingTimeoutRef.current) {
49975
+ clearTimeout(loadingTimeoutRef.current);
49976
+ loadingTimeoutRef.current = null;
49977
+ }
49978
+ setIsTransitioning(false);
49979
+ setIsNavigating(false);
49980
+ setIsCategoryLoading(false);
49981
+ if (preferredInitialTimeCategory) {
49982
+ setInitialFilter(preferredInitialTimeCategory);
49983
+ updateActiveFilter(preferredInitialTimeCategory);
49984
+ previousFilterRef.current = "";
49985
+ }
49986
+ }, [
49987
+ clearRetryTimeout,
49988
+ initialTimeFilterKey,
49989
+ preferredInitialTimeCategory,
49990
+ updateActiveFilter
49991
+ ]);
48443
49992
  console.log("[BottlenecksContent] Clip types data:", {
48444
49993
  clipTypes,
48445
49994
  clipTypesLength: clipTypes?.length,
@@ -48487,6 +50036,15 @@ var BottlenecksContent = ({
48487
50036
  }
48488
50037
  });
48489
50038
  useEffect(() => {
50039
+ if (resolvedInitialCategory) {
50040
+ if (initialFilter !== resolvedInitialCategory) {
50041
+ setInitialFilter(resolvedInitialCategory);
50042
+ }
50043
+ if (activeFilterRef.current !== resolvedInitialCategory) {
50044
+ updateActiveFilter(resolvedInitialCategory);
50045
+ }
50046
+ return;
50047
+ }
48490
50048
  if (initialFilter) {
48491
50049
  return;
48492
50050
  }
@@ -48503,7 +50061,15 @@ var BottlenecksContent = ({
48503
50061
  activeFilterRef.current = defaultCategory;
48504
50062
  return;
48505
50063
  }
48506
- }, [clipTypes, dynamicCounts, defaultCategory, initialFilter, isLowMomentsCategoryAvailable]);
50064
+ }, [
50065
+ clipTypes,
50066
+ dynamicCounts,
50067
+ defaultCategory,
50068
+ initialFilter,
50069
+ isLowMomentsCategoryAvailable,
50070
+ resolvedInitialCategory,
50071
+ updateActiveFilter
50072
+ ]);
48507
50073
  const mergedCounts = useMemo(() => {
48508
50074
  return { ...dynamicCounts };
48509
50075
  }, [dynamicCounts]);
@@ -48552,6 +50118,9 @@ var BottlenecksContent = ({
48552
50118
  const loadFirstVideoForCategory = useCallback(async (category) => {
48553
50119
  if (!workspaceId || !s3ClipsService || !isMountedRef.current || !isEffectiveShiftReady) return;
48554
50120
  const targetCategory = category || activeFilterRef.current;
50121
+ if (targetCategory === "fast-cycles" || targetCategory === "slow-cycles") {
50122
+ return;
50123
+ }
48555
50124
  const operationKey = `loadFirstVideo:${targetCategory}:${effectiveDateString}:${effectiveShiftId}`;
48556
50125
  if (loadingCategoryRef.current === targetCategory || fetchInProgressRef.current.has(operationKey)) {
48557
50126
  console.log(`[BottlenecksContent] Load first video already in progress for ${targetCategory}`);
@@ -48997,8 +50566,9 @@ var BottlenecksContent = ({
48997
50566
  });
48998
50567
  }
48999
50568
  setVisibleCategoryMetadata(categoryId, cachedMetadata);
49000
- if (autoLoadFirstVideo && cachedMetadata.length > 0 && s3ClipsService) {
49001
- const firstClipMeta = cachedMetadata[0];
50569
+ const cachedAutoloadCandidates = isInitialTimeHandoffCategory(categoryId) ? cachedMetadata.filter((clip) => isTimestampInInitialTimeHandoff(clip.clip_timestamp || clip.creation_timestamp || clip.timestamp)) : cachedMetadata;
50570
+ if (autoLoadFirstVideo && cachedAutoloadCandidates.length > 0 && s3ClipsService) {
50571
+ const firstClipMeta = cachedAutoloadCandidates[0];
49002
50572
  const firstClipId = firstClipMeta.clipId || firstClipMeta.id;
49003
50573
  const prefetchedFirstVideo = matchingLowMomentsPrefetch?.firstVideo ?? null;
49004
50574
  try {
@@ -49037,7 +50607,10 @@ var BottlenecksContent = ({
49037
50607
  endDate: `${resolvedDate}T23:59:59Z`,
49038
50608
  percentile: 10,
49039
50609
  shiftId: effectiveShiftId,
49040
- limit: 100
50610
+ limit: isInitialTimeHandoffCategory(categoryId) ? 500 : 100,
50611
+ startTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.startTime : void 0,
50612
+ endTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.endTime : void 0,
50613
+ timeFilterTimezone: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.timezone || timezone : void 0
49041
50614
  }),
49042
50615
  redirectReason: "session_expired"
49043
50616
  });
@@ -49054,10 +50627,13 @@ var BottlenecksContent = ({
49054
50627
  shift: effectiveShiftId,
49055
50628
  category: categoryId,
49056
50629
  page: 1,
49057
- limit: 100,
49058
- knownTotal: mergedCounts[categoryId] ?? null,
50630
+ limit: isInitialTimeHandoffCategory(categoryId) ? 500 : 100,
50631
+ knownTotal: isInitialTimeHandoffCategory(categoryId) ? null : mergedCounts[categoryId] ?? null,
49059
50632
  snapshotDateTime,
49060
50633
  snapshotClipId,
50634
+ startTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.startTime : void 0,
50635
+ endTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.endTime : void 0,
50636
+ timeFilterTimezone: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.timezone || timezone : void 0,
49061
50637
  sort: categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
49062
50638
  }),
49063
50639
  redirectReason: "session_expired"
@@ -49070,7 +50646,8 @@ var BottlenecksContent = ({
49070
50646
  if (categoryData.clips && isMountedRef.current) {
49071
50647
  let metadataClips;
49072
50648
  if (isPercentileCategory(categoryId)) {
49073
- metadataClips = categoryData.clips.map((clip, index) => ({
50649
+ const sortedPercentileClips = sortPercentileCycleClipsForDisplay(categoryId, categoryData.clips);
50650
+ metadataClips = sortedPercentileClips.map((clip, index) => ({
49074
50651
  id: clip.id,
49075
50652
  clipId: clip.id,
49076
50653
  clip_timestamp: clip.creation_timestamp || clip.timestamp,
@@ -49117,8 +50694,9 @@ var BottlenecksContent = ({
49117
50694
  }));
49118
50695
  setVisibleCategoryMetadata(categoryId, metadataClips);
49119
50696
  console.log(`[BottlenecksContent] Loaded metadata for ${categoryId}: ${metadataClips.length} clips`);
49120
- if (autoLoadFirstVideo && metadataClips.length > 0 && s3ClipsService) {
49121
- const firstClipMeta = metadataClips[0];
50697
+ const autoloadCandidates = isInitialTimeHandoffCategory(categoryId) ? metadataClips.filter((clip) => isTimestampInInitialTimeHandoff(clip.clip_timestamp || clip.creation_timestamp || clip.timestamp)) : metadataClips;
50698
+ if (autoLoadFirstVideo && autoloadCandidates.length > 0 && s3ClipsService) {
50699
+ const firstClipMeta = autoloadCandidates[0];
49122
50700
  const firstClipId = firstClipMeta.clipId || firstClipMeta.id;
49123
50701
  try {
49124
50702
  const video = await s3ClipsService.getClipById(firstClipId);
@@ -49152,7 +50730,7 @@ var BottlenecksContent = ({
49152
50730
  setIsCategoryLoading(false);
49153
50731
  }
49154
50732
  }
49155
- }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, idleClipSort, supabase, setVisibleCategoryMetadata, lowMomentsPrefetch, applyPrefetchedFirstVideo, applyMetadataSnapshot, isLowMomentsCategoryAvailable]);
50733
+ }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, idleClipSort, supabase, setVisibleCategoryMetadata, lowMomentsPrefetch, applyPrefetchedFirstVideo, applyMetadataSnapshot, isLowMomentsCategoryAvailable, isInitialTimeHandoffCategory, isTimestampInInitialTimeHandoff, initialTimeFilter?.endTime, initialTimeFilter?.startTime, initialTimeFilter?.timezone, timezone]);
49156
50734
  useEffect(() => {
49157
50735
  if (activeFilter !== LOW_EFFICIENCY_CATEGORY_ID || !isLowMomentsCategoryAvailable) {
49158
50736
  return;
@@ -49184,6 +50762,37 @@ var BottlenecksContent = ({
49184
50762
  applyMetadataSnapshot,
49185
50763
  isLowMomentsCategoryAvailable
49186
50764
  ]);
50765
+ useEffect(() => {
50766
+ if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !initialTimePrefetch || initialTimePrefetch.loading || initialTimePrefetch.categoryId !== activeFilter) {
50767
+ return;
50768
+ }
50769
+ if (initialTimePrefetch.metadata.length > 0) {
50770
+ applyMetadataSnapshot(
50771
+ initialTimePrefetch.categoryId,
50772
+ initialTimePrefetch.metadata,
50773
+ initialTimePrefetch.total
50774
+ );
50775
+ }
50776
+ if (initialTimePrefetch.firstVideo && isTimestampInInitialTimeHandoff(
50777
+ initialTimePrefetch.firstVideo.creation_timestamp || initialTimePrefetch.firstVideo.timestamp
50778
+ )) {
50779
+ applyPrefetchedFirstVideo(initialTimePrefetch.firstVideo);
50780
+ if (isMountedRef.current) {
50781
+ setIsCategoryLoading(false);
50782
+ setIsInitialLoading(false);
50783
+ setIsTransitioning(false);
50784
+ setIsNavigating(false);
50785
+ }
50786
+ }
50787
+ }, [
50788
+ activeFilter,
50789
+ applyMetadataSnapshot,
50790
+ applyPrefetchedFirstVideo,
50791
+ initialTimeFilter?.endTime,
50792
+ initialTimeFilter?.startTime,
50793
+ initialTimePrefetch,
50794
+ isTimestampInInitialTimeHandoff
50795
+ ]);
49187
50796
  useEffect(() => {
49188
50797
  if (previousIdleClipSortRef.current === idleClipSort) {
49189
50798
  return;
@@ -49223,7 +50832,7 @@ var BottlenecksContent = ({
49223
50832
  currentTotalRef.current = total;
49224
50833
  setCurrentTotal(total);
49225
50834
  previousFilterRef.current = activeFilter;
49226
- const metadataLoadPlan = getCategoryMetadataLoadPlanForFilterChange({
50835
+ const metadataLoadPlan = isInitialTimeHandoffCategory(activeFilter) ? { shouldLoad: false, autoLoadFirstVideo: false } : getCategoryMetadataLoadPlanForFilterChange({
49227
50836
  activeFilter,
49228
50837
  currentClipId,
49229
50838
  categoryTotal: total
@@ -49247,7 +50856,7 @@ var BottlenecksContent = ({
49247
50856
  }
49248
50857
  }
49249
50858
  }
49250
- }, [activeFilter, allVideos, mergedCounts, currentClipId, loadCategoryMetadata]);
50859
+ }, [activeFilter, allVideos, mergedCounts, currentClipId, loadCategoryMetadata, isInitialTimeHandoffCategory]);
49251
50860
  useEffect(() => {
49252
50861
  if (!currentClipId || activeFilter === "all") {
49253
50862
  return;
@@ -49275,10 +50884,19 @@ var BottlenecksContent = ({
49275
50884
  console.warn("[BottlenecksContent] Error disposing player:", e);
49276
50885
  }
49277
50886
  }
49278
- loadingTimeoutRef.current = setTimeout(() => {
50887
+ if (loadingTimeoutRef.current) {
50888
+ clearTimeout(loadingTimeoutRef.current);
50889
+ loadingTimeoutRef.current = null;
50890
+ }
50891
+ const loadingTimeout = setTimeout(() => {
50892
+ if (loadingTimeoutRef.current !== loadingTimeout) {
50893
+ return;
50894
+ }
50895
+ loadingTimeoutRef.current = null;
49279
50896
  console.warn("[BottlenecksContent] Loading timeout - clearing stuck loading state");
49280
50897
  clearLoadingState();
49281
50898
  }, 2e3);
50899
+ loadingTimeoutRef.current = loadingTimeout;
49282
50900
  if (activeFilterRef.current !== categoryId) {
49283
50901
  updateActiveFilter(categoryId);
49284
50902
  }
@@ -49298,17 +50916,20 @@ var BottlenecksContent = ({
49298
50916
  setCurrentClipId(clipId);
49299
50917
  setAllVideos([video]);
49300
50918
  setCurrentIndex(0);
50919
+ clearLoadingState();
49301
50920
  } else {
49302
50921
  throw new Error(`Failed to load video data for clip ${clipId}`);
49303
50922
  }
49304
50923
  await metadataPromise;
49305
50924
  let metadataArray = categoryMetadataRef.current;
49306
- const fallbackHasClip = fallbackMetadata?.some((clip) => clip.clipId === clipId);
49307
- if ((metadataArray.length === 0 || !metadataArray.some((clip) => clip.clipId === clipId)) && fallbackHasClip) {
50925
+ const getMetadataClipId = (clip) => clip?.clipId || clip?.id;
50926
+ const metadataHasClip = (clips) => clips.some((clip) => getMetadataClipId(clip) === clipId);
50927
+ const fallbackHasClip = fallbackMetadata?.some((clip) => getMetadataClipId(clip) === clipId);
50928
+ if ((metadataArray.length === 0 || !metadataHasClip(metadataArray)) && fallbackHasClip) {
49308
50929
  applyMetadataSnapshot(categoryId, fallbackMetadata, fallbackTotal);
49309
50930
  metadataArray = fallbackMetadata;
49310
50931
  }
49311
- if (metadataArray.length === 0 || !metadataArray.some((clip) => clip.clipId === clipId)) {
50932
+ if (metadataArray.length === 0 || !metadataHasClip(metadataArray)) {
49312
50933
  if (!fallbackHasClip) {
49313
50934
  console.warn(`[BottlenecksContent] Clip ${clipId} not found in metadata for ${categoryId} (cache hit: ${metadataArray.length > 0}) - forcing refresh`);
49314
50935
  await loadCategoryMetadata(categoryId, false, true);
@@ -49327,7 +50948,7 @@ var BottlenecksContent = ({
49327
50948
  }
49328
50949
  return;
49329
50950
  }
49330
- const clickedClipIndex = metadataArray.findIndex((clip) => clip.clipId === clipId);
50951
+ const clickedClipIndex = metadataArray.findIndex((clip) => getMetadataClipId(clip) === clipId);
49331
50952
  if (clickedClipIndex === -1) {
49332
50953
  console.warn(`[BottlenecksContent] Clip ${clipId} not found after metadata refresh`);
49333
50954
  if (!shouldUseMetadataNavigation(categoryId)) {
@@ -49715,6 +51336,32 @@ var BottlenecksContent = ({
49715
51336
  }
49716
51337
  return filteredVideos[currentIndex];
49717
51338
  }, [filteredVideos, currentIndex]);
51339
+ const currentVideoMatchesInitialTimeHandoff = useMemo(() => {
51340
+ if (!hasInitialTimeHandoff) {
51341
+ return true;
51342
+ }
51343
+ if (!currentVideo) {
51344
+ return false;
51345
+ }
51346
+ if (!isInitialTimeHandoffCategory(activeFilter) || !isInitialTimeHandoffCategory(currentVideo.type)) {
51347
+ return false;
51348
+ }
51349
+ return isTimestampInInitialTimeHandoff(
51350
+ currentVideo.creation_timestamp || currentVideo.timestamp || currentVideo.clip_end_time || currentVideo.clip_start_time
51351
+ );
51352
+ }, [
51353
+ activeFilter,
51354
+ currentVideo,
51355
+ hasInitialTimeHandoff,
51356
+ isInitialTimeHandoffCategory,
51357
+ isTimestampInInitialTimeHandoff
51358
+ ]);
51359
+ const shouldHoldInitialTimeHandoffVideo = Boolean(
51360
+ hasInitialTimeHandoff && !currentVideoMatchesInitialTimeHandoff && (isCategoryLoading || filteredVideos.length > 0 || (mergedCounts[activeFilter] || 0) > 0)
51361
+ );
51362
+ const canRenderCurrentVideo = Boolean(
51363
+ filteredVideos.length > 0 && currentVideo && !isFullscreen && !shouldHoldInitialTimeHandoffVideo
51364
+ );
49718
51365
  const currentLowEfficiencyClipId = currentVideo?.id || currentClipId || null;
49719
51366
  const isCurrentLowEfficiencyClip = Boolean(
49720
51367
  currentVideo?.type === "recent_flow_red_streak" || currentVideo?.red_flow_timeline
@@ -49881,11 +51528,111 @@ var BottlenecksContent = ({
49881
51528
  }
49882
51529
  return currentPosition;
49883
51530
  }, [activeFilter, categoryMetadata.length, currentMetadataIndex, currentPosition, shouldUseMetadataNavigation]);
49884
- const prefetchedExplorerMetadata = useMemo(() => activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && !lowMomentsPrefetch.loading && lowMomentsPrefetch.metadata.length > 0 ? { [LOW_EFFICIENCY_CATEGORY_ID]: lowMomentsPrefetch.metadata } : activeFilter === "idle_time" && categoryMetadataSort !== idleClipSort ? void 0 : buildPrefetchedExplorerMetadata(
51531
+ const initialTimePrefetchedMetadata = useMemo(() => {
51532
+ if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !initialTimePrefetch || initialTimePrefetch.loading) {
51533
+ return void 0;
51534
+ }
51535
+ if (initialTimePrefetch.metadataByCategory) {
51536
+ return initialTimePrefetch.metadataByCategory;
51537
+ }
51538
+ if (initialTimePrefetch.categoryId === activeFilter && initialTimePrefetch.metadata.length > 0) {
51539
+ return { [activeFilter]: initialTimePrefetch.metadata };
51540
+ }
51541
+ return void 0;
51542
+ }, [
49885
51543
  activeFilter,
51544
+ initialTimePrefetch,
51545
+ initialTimeFilter?.endTime,
51546
+ initialTimeFilter?.startTime
51547
+ ]);
51548
+ const prefetchedExplorerMetadata = useMemo(() => {
51549
+ if (initialTimePrefetchedMetadata) {
51550
+ return initialTimePrefetchedMetadata;
51551
+ }
51552
+ if (activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && !lowMomentsPrefetch.loading && lowMomentsPrefetch.metadata.length > 0) {
51553
+ return { [LOW_EFFICIENCY_CATEGORY_ID]: lowMomentsPrefetch.metadata };
51554
+ }
51555
+ if (activeFilter === "idle_time" && categoryMetadataSort !== idleClipSort) {
51556
+ return void 0;
51557
+ }
51558
+ const metadataSnapshot = buildPrefetchedExplorerMetadata(
51559
+ activeFilter,
51560
+ categoryMetadataCategoryId,
51561
+ categoryMetadata
51562
+ );
51563
+ if (metadataSnapshot) {
51564
+ return metadataSnapshot;
51565
+ }
51566
+ if (initialTimeFilter?.startTime && initialTimeFilter?.endTime && currentVideo && currentVideo.type === activeFilter) {
51567
+ return {
51568
+ [activeFilter]: [{
51569
+ id: currentVideo.id,
51570
+ clipId: currentVideo.id,
51571
+ clip_timestamp: currentVideo.creation_timestamp || currentVideo.timestamp,
51572
+ description: currentVideo.description,
51573
+ severity: currentVideo.severity,
51574
+ category: activeFilter,
51575
+ duration: typeof currentVideo.duration === "number" ? currentVideo.duration : typeof currentVideo.cycle_time_seconds === "number" ? currentVideo.cycle_time_seconds : void 0,
51576
+ clip_start_time: currentVideo.clip_start_time,
51577
+ clip_end_time: currentVideo.clip_end_time,
51578
+ index: 0,
51579
+ idle_start_time: currentVideo.idle_start_time,
51580
+ idle_end_time: currentVideo.idle_end_time,
51581
+ cycle_item_count: null,
51582
+ red_flow_timeline: currentVideo.red_flow_timeline,
51583
+ red_flow_severity_score: currentVideo.red_flow_severity_score,
51584
+ red_flow_output_shortfall_units: currentVideo.red_flow_output_shortfall_units,
51585
+ red_flow_worst_minute: currentVideo.red_flow_worst_minute,
51586
+ red_flow_explanation_summary: currentVideo.red_flow_explanation_summary,
51587
+ red_flow_explanation: currentVideo.red_flow_explanation
51588
+ }]
51589
+ };
51590
+ }
51591
+ return void 0;
51592
+ }, [
51593
+ activeFilter,
51594
+ categoryMetadata,
49886
51595
  categoryMetadataCategoryId,
49887
- categoryMetadata
49888
- ), [activeFilter, categoryMetadata, categoryMetadataCategoryId, categoryMetadataSort, idleClipSort, lowMomentsPrefetch, workspaceId, effectiveDateString, effectiveShiftId, isLowMomentsCategoryAvailable]);
51596
+ categoryMetadataSort,
51597
+ currentVideo,
51598
+ idleClipSort,
51599
+ initialTimePrefetchedMetadata,
51600
+ initialTimePrefetch,
51601
+ initialTimeFilter?.endTime,
51602
+ initialTimeFilter?.startTime,
51603
+ lowMomentsPrefetch,
51604
+ workspaceId,
51605
+ effectiveDateString,
51606
+ effectiveShiftId,
51607
+ isLowMomentsCategoryAvailable
51608
+ ]);
51609
+ const externallyManagedLoadingCategories = useMemo(() => {
51610
+ const managedCategories = {
51611
+ recent_flow_red_streak: Boolean(
51612
+ activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && lowMomentsPrefetch.loading
51613
+ )
51614
+ };
51615
+ const isInitialTimeHandoffPending = Boolean(
51616
+ initialTimeFilter?.startTime && initialTimeFilter?.endTime && requestedInitialCategoryCandidates.length > 0 && (!initialTimePrefetch || initialTimePrefetch.loading)
51617
+ );
51618
+ if (isInitialTimeHandoffPending) {
51619
+ requestedInitialCategoryCandidates.forEach((categoryId) => {
51620
+ managedCategories[categoryId] = true;
51621
+ });
51622
+ }
51623
+ return managedCategories;
51624
+ }, [
51625
+ activeFilter,
51626
+ effectiveDateString,
51627
+ effectiveShiftId,
51628
+ initialTimeFilter?.endTime,
51629
+ initialTimeFilter?.startTime,
51630
+ initialTimePrefetch,
51631
+ isLowMomentsCategoryAvailable,
51632
+ lowMomentsPrefetch,
51633
+ requestedInitialCategoryCandidates,
51634
+ workspaceId
51635
+ ]);
49889
51636
  const classificationClipIds = useMemo(() => {
49890
51637
  if (!idleTimeVlmEnabled) {
49891
51638
  return [];
@@ -50295,7 +52042,7 @@ var BottlenecksContent = ({
50295
52042
  /* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: "S3 configuration is required to load video clips. Please check your dashboard configuration." })
50296
52043
  ] });
50297
52044
  }
50298
- if (clipTypesLoading && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
52045
+ if (!hasInitialTimeHandoff && clipTypesLoading && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
50299
52046
  return /* @__PURE__ */ jsx("div", { className: "flex-grow p-4 flex items-center justify-center min-h-[calc(100dvh-12rem)]", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
50300
52047
  }
50301
52048
  if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
@@ -50305,7 +52052,30 @@ var BottlenecksContent = ({
50305
52052
  /* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
50306
52053
  ] });
50307
52054
  }
50308
- const categoriesToShow = clipTypes.length > 0 ? clipTypes : [];
52055
+ const categoriesToShow = (() => {
52056
+ const categories = clipTypes.length > 0 ? [...clipTypes] : [];
52057
+ const existingTypes = new Set(categories.map((category) => category.type));
52058
+ requestedInitialCategoryCandidates.forEach((categoryType) => {
52059
+ if (existingTypes.has(categoryType) || !hasInitialTimeHandoff && (mergedCounts[categoryType] || 0) <= 0) {
52060
+ return;
52061
+ }
52062
+ const fallback = CHART_HANDOFF_CATEGORY_FALLBACKS[categoryType];
52063
+ if (!fallback) {
52064
+ return;
52065
+ }
52066
+ categories.push({
52067
+ id: categoryType,
52068
+ type: categoryType,
52069
+ label: fallback.label,
52070
+ description: fallback.description,
52071
+ color: fallback.color,
52072
+ icon: fallback.icon,
52073
+ sort_order: 50
52074
+ });
52075
+ existingTypes.add(categoryType);
52076
+ });
52077
+ return categories;
52078
+ })();
50309
52079
  console.log("[BottlenecksContent] Categories to show:", {
50310
52080
  categoriesToShow,
50311
52081
  categoriesToShowLength: categoriesToShow?.length,
@@ -50324,190 +52094,204 @@ var BottlenecksContent = ({
50324
52094
  }
50325
52095
  ),
50326
52096
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:h-full", children: [
50327
- /* @__PURE__ */ jsx("div", { className: "min-w-0 w-full lg:flex-[3] lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden lg:h-full", children: filteredVideos.length > 0 && currentVideo && !isFullscreen ? /* @__PURE__ */ jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative group lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "relative w-full aspect-video lg:aspect-auto lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
50328
- /* @__PURE__ */ jsx(
50329
- "div",
50330
- {
50331
- className: "w-full h-full",
50332
- style: {
50333
- opacity: isTransitioning ? 0 : 1,
50334
- transition: "opacity 0.1s ease-in-out"
50335
- },
50336
- children: !shouldDeferPlayerRenderForCrop && /* @__PURE__ */ jsx(
50337
- CroppedVideoPlayer,
52097
+ /* @__PURE__ */ jsx(
52098
+ "div",
52099
+ {
52100
+ className: "min-w-0 w-full lg:flex-[3] lg:h-full",
52101
+ "data-qa-clips-active-filter": activeFilter || "",
52102
+ "data-qa-clips-current-video-type": currentVideo?.type || "",
52103
+ "data-qa-clips-current-video-id": currentVideo?.id || "",
52104
+ "data-qa-clips-current-clip-id": currentClipId || "",
52105
+ "data-qa-clips-filtered-count": filteredVideos.length,
52106
+ "data-qa-clips-all-count": allVideos.length,
52107
+ "data-qa-clips-current-video-matches-hour": String(currentVideoMatchesInitialTimeHandoff),
52108
+ "data-qa-clips-hold-hour-video": String(shouldHoldInitialTimeHandoffVideo),
52109
+ children: /* @__PURE__ */ jsx("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden lg:h-full", children: canRenderCurrentVideo && currentVideo ? /* @__PURE__ */ jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative group lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "relative w-full aspect-video lg:aspect-auto lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
52110
+ /* @__PURE__ */ jsx(
52111
+ "div",
50338
52112
  {
50339
- ref: videoRef,
50340
- src: currentVideo.src,
50341
- poster: "",
50342
52113
  className: "w-full h-full",
50343
- crop: workspaceCrop?.crop,
50344
- autoplay: true,
50345
- playsInline: true,
50346
- loop: false,
50347
- externalLoadingControl: true,
50348
- onReady: handleVideoReady,
50349
- onPlay: handleVideoPlay,
50350
- onPause: handleVideoPause,
50351
- onTimeUpdate: handleTimeUpdate,
50352
- onDurationChange: handleDurationChange,
50353
- onEnded: handleVideoEnded,
50354
- onError: handleVideoError,
50355
- onLoadedData: handleLoadedData,
50356
- onPlaying: handleVideoPlaying,
50357
- onLoadingChange: handleVideoLoadingChange,
50358
- onShare: handleShareClip,
50359
- isShareLoading,
50360
- isShareCopied,
50361
- timelineAnnotations: currentVideo.red_flow_timeline,
50362
- timelineExplanation: currentVideo.red_flow_explanation,
50363
- timelineTimezone: timezone,
50364
- options: videoPlayerOptions
50365
- },
50366
- `${currentVideo.id}-${playerInstanceNonce}-inline`
50367
- )
50368
- }
50369
- ),
50370
- currentVideo.type === "recent_flow_red_streak" && !shouldDeferPlayerRenderForCrop ? /* @__PURE__ */ jsx(
50371
- RedFlowDiagnosticOverlay,
50372
- {
50373
- timeline: currentVideo.red_flow_timeline,
50374
- explanation: currentVideo.red_flow_explanation,
50375
- aiSummary: currentLowEfficiencyAiSummary,
50376
- aiSummaryLoading: isCurrentLowEfficiencyAiSummaryLoading,
50377
- aiSummaryError: currentLowEfficiencyAiSummaryError,
50378
- className: "right-4 top-4"
50379
- }
50380
- ) : null,
50381
- showBlockingVideoLoader && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
50382
- !shouldDeferPlayerRenderForCrop && !isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
50383
- error && error.type === "retrying" && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
50384
- /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md" }),
50385
- /* @__PURE__ */ jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
50386
- ] }) }),
50387
- error && error.type === "fatal" && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/90 text-white p-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-md", children: [
50388
- /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 mx-auto mb-4 text-red-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 16.5c-.77.833.192 2.5 1.732 2.5z" }) }),
50389
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children: error.code === 3 ? "Stream Corrupted" : error.code === 4 ? "Format Not Supported" : error.code === 2 ? "Network Error" : error.code === 1 ? "Loading Interrupted" : "Playback Error" }),
50390
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
50391
- /* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-center", children: [
50392
- error.canSkip && /* @__PURE__ */ jsx(
50393
- "button",
50394
- {
50395
- onClick: () => {
50396
- setError(null);
50397
- videoRetryCountRef.current = 0;
50398
- handleNext();
52114
+ style: {
52115
+ opacity: isTransitioning ? 0 : 1,
52116
+ transition: "opacity 0.1s ease-in-out"
50399
52117
  },
50400
- className: "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 rounded-md text-sm font-medium transition-colors",
50401
- children: "Skip to Next Clip"
52118
+ children: !shouldDeferPlayerRenderForCrop && /* @__PURE__ */ jsx(
52119
+ CroppedVideoPlayer,
52120
+ {
52121
+ ref: videoRef,
52122
+ src: currentVideo.src,
52123
+ poster: "",
52124
+ className: "w-full h-full",
52125
+ crop: workspaceCrop?.crop,
52126
+ autoplay: true,
52127
+ playsInline: true,
52128
+ loop: false,
52129
+ externalLoadingControl: true,
52130
+ onReady: handleVideoReady,
52131
+ onPlay: handleVideoPlay,
52132
+ onPause: handleVideoPause,
52133
+ onTimeUpdate: handleTimeUpdate,
52134
+ onDurationChange: handleDurationChange,
52135
+ onEnded: handleVideoEnded,
52136
+ onError: handleVideoError,
52137
+ onLoadedData: handleLoadedData,
52138
+ onPlaying: handleVideoPlaying,
52139
+ onLoadingChange: handleVideoLoadingChange,
52140
+ onShare: handleShareClip,
52141
+ isShareLoading,
52142
+ isShareCopied,
52143
+ timelineAnnotations: currentVideo.red_flow_timeline,
52144
+ timelineExplanation: currentVideo.red_flow_explanation,
52145
+ timelineTimezone: timezone,
52146
+ options: videoPlayerOptions
52147
+ },
52148
+ `${currentVideo.id}-${playerInstanceNonce}-inline`
52149
+ )
50402
52150
  }
50403
52151
  ),
50404
- error.canRetry && /* @__PURE__ */ jsx(
50405
- "button",
52152
+ currentVideo.type === "recent_flow_red_streak" && !shouldDeferPlayerRenderForCrop ? /* @__PURE__ */ jsx(
52153
+ RedFlowDiagnosticOverlay,
50406
52154
  {
50407
- onClick: () => {
50408
- videoRetryCountRef.current = 0;
50409
- restartCurrentClipPlayback();
50410
- },
50411
- className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
50412
- children: "Retry"
52155
+ timeline: currentVideo.red_flow_timeline,
52156
+ explanation: currentVideo.red_flow_explanation,
52157
+ aiSummary: currentLowEfficiencyAiSummary,
52158
+ aiSummaryLoading: isCurrentLowEfficiencyAiSummaryLoading,
52159
+ aiSummaryError: currentLowEfficiencyAiSummaryError,
52160
+ className: "right-4 top-4"
50413
52161
  }
50414
- )
50415
- ] })
50416
- ] }) }),
50417
- (currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || currentVideo.type === "idle_time" || currentVideo.type === "low_value" ? currentVideo.type === "idle_time" ? (
50418
- // Show full colored badge for idle time
50419
- (() => {
50420
- const classification = getIdleTimeClassification(currentVideo);
50421
- const confidence = getIdleTimeConfidence(currentVideo);
50422
- const config = getRootCauseConfig(classification);
50423
- if (!config) return null;
50424
- const IconComponent = config.Icon;
50425
- return /* @__PURE__ */ jsxs(Fragment, { children: [
50426
- idleTimeVlmEnabled && /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsxs("div", { className: `inline-flex max-w-[18rem] items-center gap-1.5 rounded-md border px-2.5 py-1.5 shadow-lg ${config.bgColor} ${config.borderColor}`, children: [
50427
- /* @__PURE__ */ jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
50428
- /* @__PURE__ */ jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
50429
- ] }) }),
50430
- idleTimeVlmEnabled && confidence !== null && (() => {
50431
- const confidencePercent = confidence * 100;
50432
- let confidenceLabel = "Low";
50433
- let confidenceColor = "text-red-500";
50434
- if (confidencePercent > 95) {
50435
- confidenceLabel = "High";
50436
- confidenceColor = "text-green-500";
50437
- } else if (confidencePercent >= 91) {
50438
- confidenceLabel = "Medium";
50439
- confidenceColor = "text-yellow-500";
50440
- }
50441
- return /* @__PURE__ */ jsx("div", { className: "absolute top-3 right-3 z-10", children: /* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-black/70 backdrop-blur-sm shadow-lg border border-white/10", children: /* @__PURE__ */ jsxs("span", { className: "font-medium text-xs text-white", children: [
50442
- "AI Confidence: ",
50443
- /* @__PURE__ */ jsx("span", { className: confidenceColor, children: confidenceLabel })
50444
- ] }) }) });
52162
+ ) : null,
52163
+ showBlockingVideoLoader && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
52164
+ !shouldDeferPlayerRenderForCrop && !isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
52165
+ error && error.type === "retrying" && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
52166
+ /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md" }),
52167
+ /* @__PURE__ */ jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
52168
+ ] }) }),
52169
+ error && error.type === "fatal" && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/90 text-white p-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-md", children: [
52170
+ /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 mx-auto mb-4 text-red-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 16.5c-.77.833.192 2.5 1.732 2.5z" }) }),
52171
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children: error.code === 3 ? "Stream Corrupted" : error.code === 4 ? "Format Not Supported" : error.code === 2 ? "Network Error" : error.code === 1 ? "Loading Interrupted" : "Playback Error" }),
52172
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
52173
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-center", children: [
52174
+ error.canSkip && /* @__PURE__ */ jsx(
52175
+ "button",
52176
+ {
52177
+ onClick: () => {
52178
+ setError(null);
52179
+ videoRetryCountRef.current = 0;
52180
+ handleNext();
52181
+ },
52182
+ className: "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 rounded-md text-sm font-medium transition-colors",
52183
+ children: "Skip to Next Clip"
52184
+ }
52185
+ ),
52186
+ error.canRetry && /* @__PURE__ */ jsx(
52187
+ "button",
52188
+ {
52189
+ onClick: () => {
52190
+ videoRetryCountRef.current = 0;
52191
+ restartCurrentClipPlayback();
52192
+ },
52193
+ className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
52194
+ children: "Retry"
52195
+ }
52196
+ )
52197
+ ] })
52198
+ ] }) }),
52199
+ (currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || currentVideo.type === "idle_time" || currentVideo.type === "low_value" ? currentVideo.type === "idle_time" ? (
52200
+ // Show full colored badge for idle time
52201
+ (() => {
52202
+ const classification = getIdleTimeClassification(currentVideo);
52203
+ const confidence = getIdleTimeConfidence(currentVideo);
52204
+ const config = getRootCauseConfig(classification);
52205
+ if (!config) return null;
52206
+ const IconComponent = config.Icon;
52207
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
52208
+ idleTimeVlmEnabled && /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsxs("div", { className: `inline-flex max-w-[18rem] items-center gap-1.5 rounded-md border px-2.5 py-1.5 shadow-lg ${config.bgColor} ${config.borderColor}`, children: [
52209
+ /* @__PURE__ */ jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
52210
+ /* @__PURE__ */ jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
52211
+ ] }) }),
52212
+ idleTimeVlmEnabled && confidence !== null && (() => {
52213
+ const confidencePercent = confidence * 100;
52214
+ let confidenceLabel = "Low";
52215
+ let confidenceColor = "text-red-500";
52216
+ if (confidencePercent > 95) {
52217
+ confidenceLabel = "High";
52218
+ confidenceColor = "text-green-500";
52219
+ } else if (confidencePercent >= 91) {
52220
+ confidenceLabel = "Medium";
52221
+ confidenceColor = "text-yellow-500";
52222
+ }
52223
+ return /* @__PURE__ */ jsx("div", { className: "absolute top-3 right-3 z-10", children: /* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-black/70 backdrop-blur-sm shadow-lg border border-white/10", children: /* @__PURE__ */ jsxs("span", { className: "font-medium text-xs text-white", children: [
52224
+ "AI Confidence: ",
52225
+ /* @__PURE__ */ jsx("span", { className: confidenceColor, children: confidenceLabel })
52226
+ ] }) }) });
52227
+ })()
52228
+ ] });
50445
52229
  })()
50446
- ] });
50447
- })()
50448
- ) : /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
50449
- /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" ? "bg-purple-400" : isPercentileCategory(activeFilterRef.current) ? activeFilterRef.current === "fast-cycles" ? "bg-green-600" : activeFilterRef.current === "slow-cycles" ? "bg-red-700" : "bg-orange-500" : currentVideo.type === "cycle_completion" ? "bg-blue-600" : "bg-gray-500"} mr-2 animate-pulse` }),
50450
- (currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds ? /* @__PURE__ */ jsxs("span", { className: "opacity-90 font-mono bg-black/30 px-2 py-0.5 rounded", children: [
50451
- "Cycle time: ",
50452
- currentVideo.cycle_time_seconds.toFixed(1),
50453
- "s"
50454
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
50455
- /* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
50456
- /* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
50457
- ] })
50458
- ] }) }) : (
50459
- /* Right side display for other video types */
50460
- currentVideo.type === "idle_time" ? (
50461
- // Show full colored badge for idle time
50462
- (() => {
50463
- const classification = getIdleTimeClassification(currentVideo);
50464
- const confidence = getIdleTimeConfidence(currentVideo);
50465
- const config = getRootCauseConfig(classification);
50466
- if (!config) return null;
50467
- const IconComponent = config.Icon;
50468
- return /* @__PURE__ */ jsxs(Fragment, { children: [
50469
- idleTimeVlmEnabled && confidence !== null && /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-black/70 backdrop-blur-sm shadow-lg border border-white/10", children: /* @__PURE__ */ jsxs("span", { className: "font-medium text-xs text-white", children: [
50470
- "AI Confidence: ",
50471
- (confidence * 100).toFixed(0),
50472
- "%"
50473
- ] }) }) }),
50474
- idleTimeVlmEnabled && /* @__PURE__ */ jsx("div", { className: "absolute top-3 right-3 z-10", children: /* @__PURE__ */ jsxs("div", { className: `inline-flex max-w-[18rem] items-center gap-1.5 rounded-md border px-2.5 py-1.5 shadow-lg ${config.bgColor} ${config.borderColor}`, children: [
50475
- /* @__PURE__ */ jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
50476
- /* @__PURE__ */ jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
50477
- ] }) })
50478
- ] });
50479
- })()
50480
- ) : /* @__PURE__ */ jsx("div", { className: `absolute top-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs ${currentVideo.type === "recent_flow_red_streak" ? "left-3" : "right-3"}`, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
50481
- /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${getSeverityColor(currentVideo.severity)} mr-2 animate-pulse` }),
50482
- /* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
50483
- currentVideo.type !== "recent_flow_red_streak" && /* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
50484
- ] }) })
50485
- )
50486
- ] }) }) }) : (
50487
- /* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
50488
- hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
50489
- /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
50490
- /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
50491
- /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
50492
- ] }) }) : (
50493
- /* Priority 5.5: Show "no folder selected" if activeFilter is empty */
50494
- hasInitialLoad && !activeFilter ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
50495
- /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
50496
- /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Folder Selected" }),
50497
- /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "Please select a folder to view clips." })
50498
- ] }) }) : (
50499
- /* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
50500
- hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
52230
+ ) : /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
52231
+ /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" ? "bg-purple-400" : isPercentileCategory(activeFilterRef.current) ? activeFilterRef.current === "fast-cycles" ? "bg-green-600" : activeFilterRef.current === "slow-cycles" ? "bg-red-700" : "bg-orange-500" : currentVideo.type === "cycle_completion" ? "bg-blue-600" : "bg-gray-500"} mr-2 animate-pulse` }),
52232
+ (currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds ? /* @__PURE__ */ jsxs("span", { className: "opacity-90 font-mono bg-black/30 px-2 py-0.5 rounded", children: [
52233
+ "Cycle time: ",
52234
+ currentVideo.cycle_time_seconds.toFixed(1),
52235
+ "s"
52236
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
52237
+ /* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
52238
+ /* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
52239
+ ] })
52240
+ ] }) }) : (
52241
+ /* Right side display for other video types */
52242
+ currentVideo.type === "idle_time" ? (
52243
+ // Show full colored badge for idle time
52244
+ (() => {
52245
+ const classification = getIdleTimeClassification(currentVideo);
52246
+ const confidence = getIdleTimeConfidence(currentVideo);
52247
+ const config = getRootCauseConfig(classification);
52248
+ if (!config) return null;
52249
+ const IconComponent = config.Icon;
52250
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
52251
+ idleTimeVlmEnabled && confidence !== null && /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsx("div", { className: "inline-flex items-center gap-1.5 px-2.5 py-1.5 rounded-lg bg-black/70 backdrop-blur-sm shadow-lg border border-white/10", children: /* @__PURE__ */ jsxs("span", { className: "font-medium text-xs text-white", children: [
52252
+ "AI Confidence: ",
52253
+ (confidence * 100).toFixed(0),
52254
+ "%"
52255
+ ] }) }) }),
52256
+ idleTimeVlmEnabled && /* @__PURE__ */ jsx("div", { className: "absolute top-3 right-3 z-10", children: /* @__PURE__ */ jsxs("div", { className: `inline-flex max-w-[18rem] items-center gap-1.5 rounded-md border px-2.5 py-1.5 shadow-lg ${config.bgColor} ${config.borderColor}`, children: [
52257
+ /* @__PURE__ */ jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
52258
+ /* @__PURE__ */ jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
52259
+ ] }) })
52260
+ ] });
52261
+ })()
52262
+ ) : /* @__PURE__ */ jsx("div", { className: `absolute top-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs ${currentVideo.type === "recent_flow_red_streak" ? "left-3" : "right-3"}`, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
52263
+ /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${getSeverityColor(currentVideo.severity)} mr-2 animate-pulse` }),
52264
+ /* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
52265
+ currentVideo.type !== "recent_flow_red_streak" && /* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
52266
+ ] }) })
52267
+ )
52268
+ ] }) }) }) : shouldHoldInitialTimeHandoffVideo ? /* @__PURE__ */ jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full min-h-[220px] sm:min-h-[320px] lg:min-h-0 lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading clips..." }) }) }) }) : (
52269
+ /* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
52270
+ hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
50501
52271
  /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
50502
- /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
50503
- /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
52272
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
52273
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
50504
52274
  ] }) }) : (
50505
- /* Priority 7: Default loading state for any other case */
50506
- /* @__PURE__ */ jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full min-h-[220px] sm:min-h-[320px] lg:min-h-0 lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
52275
+ /* Priority 5.5: Show "no folder selected" if activeFilter is empty */
52276
+ hasInitialLoad && !activeFilter ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
52277
+ /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
52278
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Folder Selected" }),
52279
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "Please select a folder to view clips." })
52280
+ ] }) }) : (
52281
+ /* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
52282
+ hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
52283
+ /* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
52284
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
52285
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
52286
+ ] }) }) : (
52287
+ /* Priority 7: Default loading state for any other case */
52288
+ /* @__PURE__ */ jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative lg:h-full", children: /* @__PURE__ */ jsx("div", { className: "relative w-full min-h-[220px] sm:min-h-[320px] lg:min-h-0 lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
52289
+ )
52290
+ )
50507
52291
  )
50508
- )
50509
- )
50510
- ) }) }),
52292
+ ) })
52293
+ }
52294
+ ),
50511
52295
  /* @__PURE__ */ jsx("div", { className: "w-full lg:flex-shrink-0 lg:flex-[1] lg:min-w-[280px] lg:max-w-[320px] lg:h-full", children: triageMode ? (
50512
52296
  /* Triage Mode - Direct tile view for cycle completions and idle time */
50513
52297
  /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm h-full overflow-hidden flex flex-col", children: [
@@ -50642,7 +52426,7 @@ var BottlenecksContent = ({
50642
52426
  })),
50643
52427
  videos: allVideos || [],
50644
52428
  activeFilter,
50645
- currentVideoId: currentVideo?.id,
52429
+ currentVideoId: currentVideo?.id || currentClipId || void 0,
50646
52430
  counts: mergedCounts,
50647
52431
  isReady: hasInitialLoad,
50648
52432
  prefetchedPercentileCounts: isFastSlowClipFiltersEnabled ? prefetchedPercentileCounts || void 0 : void 0,
@@ -50656,11 +52440,9 @@ var BottlenecksContent = ({
50656
52440
  idleTimeVlmEnabled,
50657
52441
  showPercentileCycleFilters: isFastSlowClipFiltersEnabled,
50658
52442
  prefetchedClipMetadata: prefetchedExplorerMetadata,
50659
- externallyManagedLoadingCategories: {
50660
- recent_flow_red_streak: Boolean(
50661
- activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && lowMomentsPrefetch.loading
50662
- )
50663
- },
52443
+ prefetchedClipTotals: initialTimePrefetch?.loading ? void 0 : initialTimePrefetch?.totalsByCategory,
52444
+ prefetchedPercentileClips: initialTimePrefetch?.loading ? void 0 : initialTimePrefetch?.percentileClipsByCategory,
52445
+ externallyManagedLoadingCategories,
50664
52446
  activeCategoryLoading: isCategoryLoading,
50665
52447
  idleClipSort,
50666
52448
  onIdleClipSortChange: setIdleClipSort,
@@ -68453,17 +70235,7 @@ var setSessionSeenValue = (key) => {
68453
70235
  };
68454
70236
  var buildAllGreenCelebrationSeenKey = (identity) => `${ALL_GREEN_CELEBRATION_SEEN_PREFIX}${identity}`;
68455
70237
  var buildAllGreenMilestoneSeenKey = (identity, milestoneSeconds) => `${ALL_GREEN_MILESTONE_SEEN_PREFIX}${identity}:${milestoneSeconds}`;
68456
- var LINE_SELECTOR_INDICATOR_VERSION = "incident_exclamation_v1";
68457
- var LineSelectorIncidentIcon = ({ lineId }) => /* @__PURE__ */ jsx(
68458
- "span",
68459
- {
68460
- "data-testid": `line-selector-incident-icon-${lineId}`,
68461
- "aria-label": "Line needs attention",
68462
- role: "img",
68463
- className: "inline-flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full border border-rose-200 bg-white text-[13px] font-semibold leading-none text-rose-600 shadow-[0_1px_2px_rgba(15,23,42,0.06)]",
68464
- children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "-mt-px", children: "!" })
68465
- }
68466
- );
70238
+ var LINE_SELECTOR_INDICATOR_VERSION = "line_signal_dots_v1";
68467
70239
  var LoadingPageCmp = LoadingPage_default;
68468
70240
  var LoadingOverlayCmp = LoadingOverlay_default;
68469
70241
  function HomeView({
@@ -68803,8 +70575,8 @@ function HomeView({
68803
70575
  const currentIsCurrentScopeResolved = isBootstrapMonitorMode ? bootstrapMonitor.isCurrentScopeResolved : legacyIsCurrentScopeResolved;
68804
70576
  const currentMetricsError = isBootstrapMonitorMode ? bootstrapMonitor.error : legacyMetricsError;
68805
70577
  const currentRefetchMetrics = isBootstrapMonitorMode ? bootstrapMonitor.refetch : refetchLegacyMetrics;
68806
- const lineSelectorIndicatorByLine = useMemo(() => {
68807
- const indicatorByLine = /* @__PURE__ */ new Map();
70578
+ const lineSelectorSignalStatusByLine = useMemo(() => {
70579
+ const statusByLine = /* @__PURE__ */ new Map();
68808
70580
  const legend = currentEfficiencyLegend || DEFAULT_EFFICIENCY_LEGEND;
68809
70581
  const addRows = (rows) => {
68810
70582
  (rows || []).forEach((row) => {
@@ -68812,40 +70584,36 @@ function HomeView({
68812
70584
  if (!lineId) {
68813
70585
  return;
68814
70586
  }
68815
- if (row?.red_flow_incident?.active === true) {
68816
- indicatorByLine.set(lineId, "incident");
68817
- return;
68818
- }
68819
- if (indicatorByLine.get(lineId) === "incident") {
68820
- return;
68821
- }
68822
70587
  const status = getKpiSignalStatus(row?.line_signal, legend);
68823
- if (status === "attention") {
68824
- indicatorByLine.set(lineId, "red");
70588
+ if (status) {
70589
+ statusByLine.set(lineId, status);
68825
70590
  }
68826
70591
  });
68827
70592
  };
68828
70593
  addRows(currentSelectorLineMetrics);
68829
70594
  addRows(currentLineMetrics);
68830
- return indicatorByLine;
70595
+ return statusByLine;
68831
70596
  }, [currentEfficiencyLegend, currentLineMetrics, currentSelectorLineMetrics]);
68832
- const lineSelectorIndicatorStats = useMemo(() => {
68833
- let incidentLineCount = 0;
68834
- let redFlowLineCount = 0;
70597
+ const lineSelectorSignalDotStats = useMemo(() => {
70598
+ let stableSignalLineCount = 0;
70599
+ let warningSignalLineCount = 0;
70600
+ let attentionSignalLineCount = 0;
68835
70601
  visibleLineIds.forEach((lineId) => {
68836
- const indicator = lineSelectorIndicatorByLine.get(lineId);
68837
- if (indicator === "incident") {
68838
- incidentLineCount += 1;
68839
- } else if (indicator === "red") {
68840
- redFlowLineCount += 1;
70602
+ const status = lineSelectorSignalStatusByLine.get(lineId);
70603
+ if (status === "stable") {
70604
+ stableSignalLineCount += 1;
70605
+ } else if (status === "warning") {
70606
+ warningSignalLineCount += 1;
70607
+ } else if (status === "attention") {
70608
+ attentionSignalLineCount += 1;
68841
70609
  }
68842
70610
  });
68843
70611
  return {
68844
- incidentLineCount,
68845
- redFlowLineCount,
68846
- hasAnyIncident: incidentLineCount > 0
70612
+ stableSignalLineCount,
70613
+ warningSignalLineCount,
70614
+ attentionSignalLineCount
68847
70615
  };
68848
- }, [lineSelectorIndicatorByLine, visibleLineIds]);
70616
+ }, [lineSelectorSignalStatusByLine, visibleLineIds]);
68849
70617
  const metricsDisplayNames = useMemo(() => {
68850
70618
  const nextDisplayNames = {};
68851
70619
  currentWorkspaceMetrics.forEach((workspace) => {
@@ -69702,12 +71470,13 @@ function HomeView({
69702
71470
  new_line_ids: normalizedLineIds,
69703
71471
  selected_line_count: normalizedLineIds.length,
69704
71472
  selection_mode: isAllLinesSelection(normalizedLineIds) ? "all" : normalizedLineIds.length === 1 ? "single" : "custom",
69705
- incident_line_count: lineSelectorIndicatorStats.incidentLineCount,
69706
- red_flow_line_count: lineSelectorIndicatorStats.redFlowLineCount,
71473
+ stable_signal_line_count: lineSelectorSignalDotStats.stableSignalLineCount,
71474
+ warning_signal_line_count: lineSelectorSignalDotStats.warningSignalLineCount,
71475
+ attention_signal_line_count: lineSelectorSignalDotStats.attentionSignalLineCount,
69707
71476
  selector_indicator_version: LINE_SELECTOR_INDICATOR_VERSION,
69708
71477
  line_name: getLineSelectionLabel(normalizedLineIds)
69709
71478
  });
69710
- }, [factoryViewId, getLineSelectionLabel, getTrackedLineScope, lineSelectorIndicatorStats, selectedLineIds, selectedLineIdsKey, visibleLineIds]);
71479
+ }, [factoryViewId, getLineSelectionLabel, getTrackedLineScope, lineSelectorSignalDotStats, selectedLineIds, selectedLineIdsKey, visibleLineIds]);
69711
71480
  useCallback(() => {
69712
71481
  updateSelectedLineIds(visibleLineIds);
69713
71482
  }, [updateSelectedLineIds, visibleLineIds]);
@@ -69766,8 +71535,9 @@ function HomeView({
69766
71535
  selected_line_ids: selectedLineIds,
69767
71536
  selected_line_count: selectedLineIds.length,
69768
71537
  is_all_lines: isAllLinesSelection(selectedLineIds),
69769
- incident_line_count: lineSelectorIndicatorStats.incidentLineCount,
69770
- red_flow_line_count: lineSelectorIndicatorStats.redFlowLineCount,
71538
+ stable_signal_line_count: lineSelectorSignalDotStats.stableSignalLineCount,
71539
+ warning_signal_line_count: lineSelectorSignalDotStats.warningSignalLineCount,
71540
+ attention_signal_line_count: lineSelectorSignalDotStats.attentionSignalLineCount,
69771
71541
  selector_indicator_version: LINE_SELECTOR_INDICATOR_VERSION
69772
71542
  });
69773
71543
  }
@@ -69809,7 +71579,8 @@ function HomeView({
69809
71579
  ] }),
69810
71580
  /* @__PURE__ */ jsx("div", { className: "max-h-56 space-y-0.5 overflow-y-auto pr-1", children: visibleLineIds.map((lineId) => {
69811
71581
  const isChecked = pendingSelectedLineIds.includes(lineId);
69812
- const selectorIndicator = lineSelectorIndicatorByLine.get(lineId);
71582
+ const signalStatus = lineSelectorSignalStatusByLine.get(lineId);
71583
+ const signalDotClass = signalStatus === "stable" ? "bg-green-500" : signalStatus === "warning" ? "bg-yellow-400" : signalStatus === "attention" ? "bg-red-500" : "";
69813
71584
  const lineLabel = mergedLineNames[lineId] || `Line ${lineId.substring(0, 4)}`;
69814
71585
  return /* @__PURE__ */ jsxs(
69815
71586
  "label",
@@ -69837,11 +71608,11 @@ function HomeView({
69837
71608
  }
69838
71609
  ),
69839
71610
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: lineLabel }),
69840
- /* @__PURE__ */ jsx("span", { className: "flex h-5 w-5 flex-shrink-0 items-center justify-center", children: selectorIndicator === "incident" ? /* @__PURE__ */ jsx(LineSelectorIncidentIcon, { lineId }) : selectorIndicator === "red" && !lineSelectorIndicatorStats.hasAnyIncident ? /* @__PURE__ */ jsx(
71611
+ /* @__PURE__ */ jsx("span", { className: "flex h-2.5 w-2.5 flex-shrink-0 items-center justify-center", children: signalStatus ? /* @__PURE__ */ jsx(
69841
71612
  "span",
69842
71613
  {
69843
71614
  "data-testid": `line-selector-signal-dot-${lineId}`,
69844
- className: "h-2 w-2 rounded-full bg-red-500",
71615
+ className: `h-2 w-2 rounded-full ${signalDotClass}`,
69845
71616
  "aria-hidden": "true"
69846
71617
  }
69847
71618
  ) : null })
@@ -69873,8 +71644,8 @@ function HomeView({
69873
71644
  mergedLineNames,
69874
71645
  selectedLineIds,
69875
71646
  pendingSelectedLineIds,
69876
- lineSelectorIndicatorByLine,
69877
- lineSelectorIndicatorStats,
71647
+ lineSelectorSignalStatusByLine,
71648
+ lineSelectorSignalDotStats,
69878
71649
  displayMode,
69879
71650
  slideshowActiveLineId,
69880
71651
  visibleLineIds,
@@ -70474,9 +72245,7 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
70474
72245
  await preInitializeWorkspaceDisplayNames(selectedLineId);
70475
72246
  }
70476
72247
  } else if (lineIdArray.length > 0) {
70477
- await Promise.all(
70478
- lineIdArray.map((lineId) => preInitializeWorkspaceDisplayNames(lineId))
70479
- );
72248
+ await preInitializeWorkspaceDisplayNamesForLines(lineIdArray);
70480
72249
  } else {
70481
72250
  await preInitializeWorkspaceDisplayNames();
70482
72251
  }
@@ -80940,7 +82709,6 @@ var TargetsViewUI = ({
80940
82709
  skuRequired = false,
80941
82710
  onUpdateWorkspaceSelectedSku
80942
82711
  }) => {
80943
- const { displayNames: workspaceDisplayNames } = useWorkspaceDisplayNames();
80944
82712
  const mobileMenuContext = useMobileMenu();
80945
82713
  useHideMobileHeader(!!mobileMenuContext);
80946
82714
  if (isLoading) {
@@ -81122,7 +82890,7 @@ var TargetsViewUI = ({
81122
82890
  ] })
81123
82891
  ] }) }),
81124
82892
  /* @__PURE__ */ jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => {
81125
- const formattedName = workspaceDisplayNames[`${lineId}_${workspace.name}`] || formatWorkspaceName(workspace.name, lineId);
82893
+ const formattedName = workspace.displayName || formatWorkspaceName(workspace.name, lineId);
81126
82894
  const realSkuOptions = (workspace.skuRows || []).filter((r2) => !r2.is_dummy);
81127
82895
  const showSkuDropdown = !!onUpdateWorkspaceSelectedSku && realSkuOptions.length >= 1;
81128
82896
  const isPlanLocked = !!workspace.productionPlanLock?.locked;
@@ -81673,6 +83441,7 @@ var TargetsView = ({
81673
83441
  return {
81674
83442
  id: ws.id,
81675
83443
  name: ws.workspace_id,
83444
+ displayName: ws.display_name || ws.workspace_id,
81676
83445
  targetPPH: selectedRow?.pph_threshold ?? (threshold?.pph_threshold ? Math.round(threshold.pph_threshold) : ""),
81677
83446
  targetCycleTime: selectedRow?.ideal_cycle_time ?? (threshold?.ideal_cycle_time ?? ""),
81678
83447
  targetDayOutput: selectedRow?.total_day_output ?? (threshold?.total_day_output ?? ""),
@@ -81731,6 +83500,7 @@ var TargetsView = ({
81731
83500
  return {
81732
83501
  id: ws.id,
81733
83502
  name: ws.workspace_id,
83503
+ displayName: ws.display_name || ws.workspace_id,
81734
83504
  targetPPH: "",
81735
83505
  targetCycleTime: "",
81736
83506
  targetDayOutput: "",
@@ -82211,15 +83981,31 @@ var TargetsView = ({
82211
83981
  const handleUpdateWorkspaceDisplayName = useCallback(async (workspaceId, displayName) => {
82212
83982
  try {
82213
83983
  const updated = await workspaceService.updateWorkspaceDisplayName(workspaceId, displayName);
83984
+ const nextDisplayName = updated?.display_name || displayName;
83985
+ setAllShiftsData((prev) => {
83986
+ const next = { ...prev };
83987
+ Object.entries(prev).forEach(([shiftId, shiftData]) => {
83988
+ const numericShiftId = Number(shiftId);
83989
+ const updatedShiftData = {};
83990
+ Object.entries(shiftData).forEach(([lineId, line]) => {
83991
+ updatedShiftData[lineId] = {
83992
+ ...line,
83993
+ workspaces: line.workspaces.map(
83994
+ (ws) => ws.id === workspaceId ? { ...ws, displayName: nextDisplayName } : ws
83995
+ )
83996
+ };
83997
+ });
83998
+ next[numericShiftId] = updatedShiftData;
83999
+ });
84000
+ return next;
84001
+ });
82214
84002
  if (updated?.line_id && updated?.workspace_id) {
82215
84003
  upsertWorkspaceDisplayNameInCache({
82216
84004
  lineId: updated.line_id,
82217
84005
  workspaceId: updated.workspace_id,
82218
- displayName: updated?.display_name || displayName,
84006
+ displayName: nextDisplayName,
82219
84007
  enabled: updated?.enable
82220
84008
  });
82221
- } else {
82222
- await forceRefreshWorkspaceDisplayNames();
82223
84009
  }
82224
84010
  toast.success("Workspace name updated successfully");
82225
84011
  } catch (error) {
@@ -82263,9 +84049,8 @@ var TargetsView = ({
82263
84049
  }
82264
84050
  );
82265
84051
  };
82266
- var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
82267
- var TargetsView_default = TargetsViewWithDisplayNames;
82268
- var AuthenticatedTargetsView = withAuth(React125__default.memo(TargetsViewWithDisplayNames));
84052
+ var TargetsView_default = TargetsView;
84053
+ var AuthenticatedTargetsView = withAuth(React125__default.memo(TargetsView));
82269
84054
  function useTimezone(options = {}) {
82270
84055
  const dashboardConfig = useDashboardConfig();
82271
84056
  const workspaceConfig = useWorkspaceConfig();
@@ -82445,17 +84230,22 @@ var WorkspaceHourSummaryPanel = ({
82445
84230
  return;
82446
84231
  }
82447
84232
  autoSummaryKeyRef.current = requestKey;
82448
- void summarize({
82449
- workspaceId,
82450
- companyId,
82451
- date,
82452
- shiftId,
82453
- hourIndex: selectedHour.hourIndex,
82454
- hourStart: selectedHour.startTime,
82455
- hourEnd: selectedHour.endTime,
82456
- forceRefresh: false,
82457
- selectionSource: selectedHour.source ?? "unknown"
82458
- }).catch(() => void 0);
84233
+ const summaryTimer = window.setTimeout(() => {
84234
+ void summarize({
84235
+ workspaceId,
84236
+ companyId,
84237
+ date,
84238
+ shiftId,
84239
+ hourIndex: selectedHour.hourIndex,
84240
+ hourStart: selectedHour.startTime,
84241
+ hourEnd: selectedHour.endTime,
84242
+ forceRefresh: false,
84243
+ selectionSource: selectedHour.source ?? "unknown"
84244
+ }).catch(() => void 0);
84245
+ }, 1500);
84246
+ return () => {
84247
+ window.clearTimeout(summaryTimer);
84248
+ };
82459
84249
  }, [canSummarize, companyId, date, selectedHour, selectedKey, shiftId, summarize, workspaceId]);
82460
84250
  if (!selectedHour) {
82461
84251
  return null;
@@ -82639,10 +84429,18 @@ var WorkspaceDetailView = ({
82639
84429
  setSelectedHour(null);
82640
84430
  }, [workspaceId, date, shift]);
82641
84431
  const dashboardConfig = useDashboardConfig();
84432
+ const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
82642
84433
  const { legend: efficiencyLegend } = useEfficiencyLegend();
82643
84434
  const prewarmedClipsRef = useRef(/* @__PURE__ */ new Set());
82644
84435
  const prewarmInFlightRef = useRef(/* @__PURE__ */ new Set());
82645
84436
  const [lowMomentsPrefetch, setLowMomentsPrefetch] = useState(null);
84437
+ const selectedHourClipPrefetchInFlightRef = useRef(/* @__PURE__ */ new Set());
84438
+ const selectedHourClipPrefetchAbortRef = useRef(null);
84439
+ const [initialTimePrefetch, setInitialTimePrefetch] = useState(null);
84440
+ const {
84441
+ isFastSlowClipFiltersEnabled,
84442
+ isResolved: isFastSlowClipFiltersResolved
84443
+ } = useCompanyFastSlowClipFiltersEnabled();
82646
84444
  const [aiSummaryHour, setAiSummaryHour] = useState(null);
82647
84445
  const buildHourlyOutputActionTrackingProps = useCallback((payload) => ({
82648
84446
  workspace_id: workspaceId,
@@ -82672,24 +84470,286 @@ var WorkspaceDetailView = ({
82672
84470
  timezone,
82673
84471
  workspaceId
82674
84472
  ]);
84473
+ const prefetchClipsForSelectedHour = useCallback((hour) => {
84474
+ if (!isClipsEnabled || !dashboardConfig?.s3Config || !workspaceId || !supabase) {
84475
+ return;
84476
+ }
84477
+ const resolvedDate = date || getOperationalDate(timezone);
84478
+ const resolvedShiftId = parsedShiftId ?? selectedShift;
84479
+ if (!resolvedDate || resolvedShiftId === null || resolvedShiftId === void 0) {
84480
+ return;
84481
+ }
84482
+ const categoryId = "cycle_completion";
84483
+ const regularCategoryIds = ["cycle_completion", "idle_time", "recent_flow_red_streak"];
84484
+ const percentileCategoryIds = isFastSlowClipFiltersEnabled ? ["fast-cycles", "slow-cycles"] : [];
84485
+ const allPrefetchCategoryIds = [...regularCategoryIds, ...percentileCategoryIds];
84486
+ const effectiveTimezone = hour.timezone || timezone;
84487
+ const prefetchKey = [
84488
+ workspaceId,
84489
+ resolvedDate,
84490
+ resolvedShiftId,
84491
+ hour.startTime,
84492
+ hour.endTime,
84493
+ effectiveTimezone || "",
84494
+ allPrefetchCategoryIds.join(",")
84495
+ ].join("|");
84496
+ if (initialTimePrefetch?.key === prefetchKey && !initialTimePrefetch.loading) {
84497
+ return;
84498
+ }
84499
+ if (selectedHourClipPrefetchInFlightRef.current.has(prefetchKey)) {
84500
+ return;
84501
+ }
84502
+ selectedHourClipPrefetchAbortRef.current?.abort();
84503
+ const controller = new AbortController();
84504
+ selectedHourClipPrefetchAbortRef.current = controller;
84505
+ selectedHourClipPrefetchInFlightRef.current.add(prefetchKey);
84506
+ const existingSnapshot = initialTimePrefetch?.key === prefetchKey ? initialTimePrefetch : null;
84507
+ setInitialTimePrefetch({
84508
+ key: prefetchKey,
84509
+ categoryId,
84510
+ metadata: existingSnapshot?.metadata || [],
84511
+ metadataByCategory: existingSnapshot?.metadataByCategory || {},
84512
+ totalsByCategory: existingSnapshot?.totalsByCategory || {},
84513
+ percentileClipsByCategory: existingSnapshot?.percentileClipsByCategory || {},
84514
+ firstVideo: existingSnapshot?.firstVideo || null,
84515
+ total: existingSnapshot?.total || 0,
84516
+ loading: true,
84517
+ error: null
84518
+ });
84519
+ const s3Service = videoPrefetchManager.getS3Service(dashboardConfig);
84520
+ const fetchHourlySnapshot = async () => {
84521
+ try {
84522
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
84523
+ method: "POST",
84524
+ headers: {
84525
+ "Content-Type": "application/json"
84526
+ },
84527
+ body: JSON.stringify({
84528
+ action: "hourly-snapshot",
84529
+ workspaceId,
84530
+ date: resolvedDate,
84531
+ shift: resolvedShiftId,
84532
+ startTime: hour.startTime,
84533
+ endTime: hour.endTime,
84534
+ timeFilterTimezone: effectiveTimezone
84535
+ }),
84536
+ signal: controller.signal,
84537
+ redirectReason: "session_expired"
84538
+ });
84539
+ if (!response.ok) {
84540
+ return false;
84541
+ }
84542
+ const snapshot = await response.json();
84543
+ const metadataByCategory = snapshot?.metadataByCategory && typeof snapshot.metadataByCategory === "object" ? snapshot.metadataByCategory : {};
84544
+ const percentileClipsByCategory = snapshot?.percentileClipsByCategory && typeof snapshot.percentileClipsByCategory === "object" ? snapshot.percentileClipsByCategory : {};
84545
+ const totalsByCategory = snapshot?.totalsByCategory && typeof snapshot.totalsByCategory === "object" ? Object.entries(snapshot.totalsByCategory).reduce((accumulator, [key, value]) => {
84546
+ const total = Number(value);
84547
+ accumulator[key] = Number.isFinite(total) ? Math.max(0, total) : 0;
84548
+ return accumulator;
84549
+ }, {}) : {};
84550
+ const snapshotCategoryId = typeof snapshot?.categoryId === "string" ? snapshot.categoryId : categoryId;
84551
+ const metadata = Array.isArray(snapshot?.metadata) ? snapshot.metadata : Array.isArray(metadataByCategory[snapshotCategoryId]) ? metadataByCategory[snapshotCategoryId] : [];
84552
+ if (controller.signal.aborted) {
84553
+ return true;
84554
+ }
84555
+ setInitialTimePrefetch({
84556
+ key: prefetchKey,
84557
+ categoryId: snapshotCategoryId,
84558
+ metadata,
84559
+ metadataByCategory,
84560
+ totalsByCategory,
84561
+ percentileClipsByCategory,
84562
+ firstVideo: snapshot?.firstVideo || null,
84563
+ total: typeof totalsByCategory[snapshotCategoryId] === "number" ? totalsByCategory[snapshotCategoryId] : metadata.length,
84564
+ loading: false,
84565
+ error: null
84566
+ });
84567
+ return true;
84568
+ } catch (error2) {
84569
+ if (error2.name === "AbortError") {
84570
+ return true;
84571
+ }
84572
+ console.warn("[WorkspaceDetailView] Hourly clips snapshot failed; falling back to category prefetch:", error2);
84573
+ return false;
84574
+ }
84575
+ };
84576
+ const fetchRegularCategory = async (prefetchCategoryId) => {
84577
+ const metadataResponse = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
84578
+ method: "POST",
84579
+ headers: {
84580
+ "Content-Type": "application/json"
84581
+ },
84582
+ body: JSON.stringify({
84583
+ action: "clip-metadata",
84584
+ workspaceId,
84585
+ date: resolvedDate,
84586
+ shift: resolvedShiftId,
84587
+ category: prefetchCategoryId,
84588
+ page: 1,
84589
+ limit: 500,
84590
+ knownTotal: null,
84591
+ startTime: hour.startTime,
84592
+ endTime: hour.endTime,
84593
+ timeFilterTimezone: effectiveTimezone,
84594
+ sort: prefetchCategoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : "latest"
84595
+ }),
84596
+ signal: controller.signal,
84597
+ redirectReason: "session_expired"
84598
+ });
84599
+ if (!metadataResponse.ok) {
84600
+ throw new Error(`Hourly clips metadata prefetch failed for ${prefetchCategoryId}: ${metadataResponse.status}`);
84601
+ }
84602
+ const metadataData = await metadataResponse.json();
84603
+ return {
84604
+ categoryId: prefetchCategoryId,
84605
+ clips: Array.isArray(metadataData?.clips) ? metadataData.clips : [],
84606
+ total: typeof metadataData?.total === "number" ? metadataData.total : 0
84607
+ };
84608
+ };
84609
+ const fetchPercentileCategory = async (prefetchCategoryId) => {
84610
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
84611
+ method: "POST",
84612
+ headers: {
84613
+ "Content-Type": "application/json"
84614
+ },
84615
+ body: JSON.stringify({
84616
+ action: "percentile-clips",
84617
+ workspaceId,
84618
+ startDate: `${resolvedDate}T00:00:00Z`,
84619
+ endDate: `${resolvedDate}T23:59:59Z`,
84620
+ percentile: 10,
84621
+ shiftId: resolvedShiftId,
84622
+ limit: 500,
84623
+ startTime: hour.startTime,
84624
+ endTime: hour.endTime,
84625
+ timeFilterTimezone: effectiveTimezone,
84626
+ percentileAction: prefetchCategoryId
84627
+ }),
84628
+ signal: controller.signal,
84629
+ redirectReason: "session_expired"
84630
+ });
84631
+ if (!response.ok) {
84632
+ throw new Error(`Hourly percentile clips prefetch failed for ${prefetchCategoryId}: ${response.status}`);
84633
+ }
84634
+ const data = await response.json();
84635
+ return {
84636
+ categoryId: prefetchCategoryId,
84637
+ clips: Array.isArray(data?.clips) ? data.clips : [],
84638
+ total: typeof data?.total === "number" ? data.total : 0
84639
+ };
84640
+ };
84641
+ const runPrefetch = async () => {
84642
+ try {
84643
+ const snapshotLoaded = await fetchHourlySnapshot();
84644
+ if (snapshotLoaded) {
84645
+ return;
84646
+ }
84647
+ const cycleCompletionPromise = fetchRegularCategory("cycle_completion");
84648
+ let resolvedFirstVideo = null;
84649
+ const firstVideoPromise = cycleCompletionPromise.then(async (cycleResponse) => {
84650
+ const firstClipId = cycleResponse.clips[0]?.clipId || cycleResponse.clips[0]?.id || null;
84651
+ if (!firstClipId) {
84652
+ return null;
84653
+ }
84654
+ const video = await s3Service.getClipById(firstClipId);
84655
+ resolvedFirstVideo = video;
84656
+ if (!controller.signal.aborted) {
84657
+ setInitialTimePrefetch((prev) => prev?.key === prefetchKey ? {
84658
+ ...prev,
84659
+ firstVideo: video
84660
+ } : prev);
84661
+ }
84662
+ return video;
84663
+ }).catch((error2) => {
84664
+ if (error2.name !== "AbortError") {
84665
+ console.warn("[WorkspaceDetailView] Hourly first clip prefetch failed:", error2);
84666
+ }
84667
+ return null;
84668
+ });
84669
+ const percentileResponsesPromise = Promise.all(percentileCategoryIds.map(fetchPercentileCategory));
84670
+ const regularResponses = await Promise.all([
84671
+ cycleCompletionPromise,
84672
+ fetchRegularCategory("idle_time"),
84673
+ fetchRegularCategory("recent_flow_red_streak")
84674
+ ]);
84675
+ const percentileResponses = await percentileResponsesPromise;
84676
+ const metadataByCategory = regularResponses.reduce((accumulator, response) => {
84677
+ accumulator[response.categoryId] = response.clips;
84678
+ return accumulator;
84679
+ }, {});
84680
+ const percentileClipsByCategory = percentileResponses.reduce((accumulator, response) => {
84681
+ accumulator[response.categoryId] = response.clips;
84682
+ return accumulator;
84683
+ }, {});
84684
+ const totalsByCategory = [...regularResponses, ...percentileResponses].reduce((accumulator, response) => {
84685
+ accumulator[response.categoryId] = Math.max(0, Number(response.total || 0));
84686
+ return accumulator;
84687
+ }, {});
84688
+ const metadata = metadataByCategory[categoryId] || [];
84689
+ if (controller.signal.aborted) {
84690
+ return;
84691
+ }
84692
+ setInitialTimePrefetch({
84693
+ key: prefetchKey,
84694
+ categoryId,
84695
+ metadata,
84696
+ metadataByCategory,
84697
+ totalsByCategory,
84698
+ percentileClipsByCategory,
84699
+ firstVideo: resolvedFirstVideo,
84700
+ total: typeof totalsByCategory[categoryId] === "number" ? totalsByCategory[categoryId] : metadata.length,
84701
+ loading: false,
84702
+ error: null
84703
+ });
84704
+ void firstVideoPromise;
84705
+ } catch (error2) {
84706
+ if (error2.name === "AbortError") {
84707
+ return;
84708
+ }
84709
+ console.warn("[WorkspaceDetailView] Hourly clips prefetch failed:", error2);
84710
+ setInitialTimePrefetch((prev) => prev?.key === prefetchKey ? {
84711
+ ...prev,
84712
+ loading: false,
84713
+ error: error2 instanceof Error ? error2.message : "Hourly clips prefetch failed"
84714
+ } : prev);
84715
+ } finally {
84716
+ selectedHourClipPrefetchInFlightRef.current.delete(prefetchKey);
84717
+ }
84718
+ };
84719
+ void runPrefetch();
84720
+ }, [
84721
+ dashboardConfig,
84722
+ date,
84723
+ initialTimePrefetch,
84724
+ isFastSlowClipFiltersEnabled,
84725
+ isClipsEnabled,
84726
+ parsedShiftId,
84727
+ selectedShift,
84728
+ supabase,
84729
+ timezone,
84730
+ workspaceId
84731
+ ]);
82675
84732
  const handleOutputHourSelect = useCallback((payload) => {
82676
- setSelectedHour({
84733
+ const hour = {
82677
84734
  source: "output",
82678
84735
  hourIndex: payload.hourIndex,
82679
84736
  timeRange: payload.timeRange,
82680
84737
  startTime: payload.startTime,
82681
84738
  endTime: payload.endTime,
84739
+ timezone: payload.timezone,
82682
84740
  status: payload.status,
82683
84741
  output: payload.output,
82684
84742
  target: payload.target
82685
- });
82686
- }, []);
84743
+ };
84744
+ setSelectedHour(hour);
84745
+ prefetchClipsForSelectedHour(hour);
84746
+ }, [prefetchClipsForSelectedHour]);
82687
84747
  const handleAiSummaryClick = useCallback((payload) => {
82688
84748
  trackCoreEvent("Hourly Output Ask AI Clicked", buildHourlyOutputActionTrackingProps(payload));
82689
84749
  setAiSummaryHour(payload);
82690
84750
  }, [buildHourlyOutputActionTrackingProps]);
82691
84751
  const handleCycleHourSelect = useCallback((payload) => {
82692
- setSelectedHour({
84752
+ const hour = {
82693
84753
  source: "cycle",
82694
84754
  hourIndex: payload.hourIndex,
82695
84755
  timeRange: payload.timeRange,
@@ -82699,17 +84759,23 @@ var WorkspaceDetailView = ({
82699
84759
  cycleTime: payload.cycleTime,
82700
84760
  idealCycleTime: payload.idealCycleTime,
82701
84761
  idleMinutes: payload.idleMinutes
82702
- });
82703
- }, []);
84762
+ };
84763
+ setSelectedHour(hour);
84764
+ prefetchClipsForSelectedHour(hour);
84765
+ }, [prefetchClipsForSelectedHour]);
82704
84766
  const handleOpenClipsForHour = useCallback((hour) => {
84767
+ prefetchClipsForSelectedHour(hour);
82705
84768
  setPendingClipHourFilter({
82706
84769
  startTime: hour.startTime,
82707
84770
  endTime: hour.endTime,
82708
84771
  sourceLabel: hour.timeRange,
82709
- status: hour.status === "below_target" || hour.status === "above_standard" ? "below_target" : "met_target"
84772
+ status: hour.status === "below_target" || hour.status === "above_standard" ? "below_target" : "met_target",
84773
+ timezone: hour.timezone,
84774
+ categoryId: "cycle_completion",
84775
+ categoryIds: ["cycle_completion", "fast-cycles", "slow-cycles", "idle_time", "recent_flow_red_streak"]
82710
84776
  });
82711
84777
  setActiveTab("bottlenecks");
82712
- }, []);
84778
+ }, [prefetchClipsForSelectedHour]);
82713
84779
  const handleWatchClipsFromChart = useCallback((payload) => {
82714
84780
  trackCoreEvent("Hourly Output Watch Clips Clicked", buildHourlyOutputActionTrackingProps(payload));
82715
84781
  handleOpenClipsForHour({
@@ -82718,6 +84784,7 @@ var WorkspaceDetailView = ({
82718
84784
  timeRange: payload.timeRange,
82719
84785
  startTime: payload.startTime,
82720
84786
  endTime: payload.endTime,
84787
+ timezone: payload.timezone,
82721
84788
  status: payload.status,
82722
84789
  output: payload.output,
82723
84790
  target: payload.target
@@ -82740,11 +84807,6 @@ var WorkspaceDetailView = ({
82740
84807
  startDate: isFullRange ? void 0 : rangeStart,
82741
84808
  endDate: isFullRange ? void 0 : rangeEnd
82742
84809
  });
82743
- const {
82744
- isFastSlowClipFiltersEnabled,
82745
- isResolved: isFastSlowClipFiltersResolved
82746
- } = useCompanyFastSlowClipFiltersEnabled();
82747
- const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
82748
84810
  dashboardConfig?.supervisorConfig?.enabled || false;
82749
84811
  const routedLineId = lineId || selectedLineId;
82750
84812
  const latestCachedDetailedMetrics = useMemo(() => {
@@ -84491,6 +86553,7 @@ var WorkspaceDetailView = ({
84491
86553
  workspaceMetrics: detailedWorkspaceMetrics || void 0,
84492
86554
  prefetchedPercentileCounts: isFastSlowClipFiltersEnabled ? prefetchedPercentileCounts : null,
84493
86555
  lowMomentsPrefetch,
86556
+ initialTimePrefetch,
84494
86557
  initialTimeFilter: pendingClipHourFilter,
84495
86558
  className: "h-[calc(100vh-10rem)]"
84496
86559
  }
@@ -94048,4 +96111,6 @@ var RecentFlowSnapshotGrid = ({
94048
96111
  );
94049
96112
  };
94050
96113
 
94051
- export { ACTION_FAMILIES, ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AvatarUpload, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, ClipsCostView_default as ClipsCostView, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EFFICIENCY_ON_TRACK_THRESHOLD, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, FittingTitle, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, HourlyUptimeChart, ISTTimer_default as ISTTimer, IdleTimeVlmConfigProvider, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPI_SIGNAL_LABELS, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend5 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LineOvertakeNotificationManager, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, MobileMenuProvider, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlantHeadView_default as PlantHeadView, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProductionPlanApiError, ProductionPlanView_default as ProductionPlanView, ProfileView_default as ProfileView, ROOT_DASHBOARD_EVENT_NAMES, RecentFlowSnapshotGrid, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SENTRY_HANDLED_EVENT_SESSION_LIMIT, SENTRY_HANDLED_EVENT_WINDOW_MS, SENTRY_QUOTA_STORAGE_KEY, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, SettingsPopup, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UptimeDonutChart, UptimeLineChart, UptimeMetricCards, UserAvatar, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceCycleTimeMetricCards, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, addSentryBreadcrumb, aggregateKPIsFromLineMetricsRows, aggregateLineSignals, alertsService, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, awardsService, buildDateKey, buildKPIsFromLineMetricsRow, buildKpiLineHierarchy, buildLegacyLineOvertakeEventKey, buildLineLeaderboardRows, buildLineOvertakeEventKey, buildLineSkuBreakdown, buildShiftGroupsKey, canPermissionEditProductionPlan, canRoleAccessDashboardPath, canRoleAccessTeamManagement, canRoleAssignFactories, canRoleAssignLines, canRoleChangeRole, canRoleEditProductionPlan, canRoleInviteRole, canRoleManageCompany, canRoleManageTargets, canRoleManageUsers, canRoleRemoveUser, canRoleViewClipsCost, canRoleViewUsageStats, captureHandledFrontendException, captureSentryException, captureSentryMessage, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearSentryContext, clearWorkspaceDisplayNamesCache, cn, combineLineMetricsRows, countRealSkus, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStorageService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, detectLineOvertakeEvents, fetchIdleTimeReasons, fetchLineDummySkuId, fetchLineSkuCatalog, filterDataByDateKeyRange, filterRealSkuBreakdown, forceRefreshWorkspaceDisplayNames, formatAwardMonth, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration2 as formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getActionDisplayName, getActiveShift, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAssignableRoles, getAssignmentColumnLabel, getAvailableShiftIds, getAwardBadgeType, getAwardDescription, getAwardTitle, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getCurrentWeekFullRange, getCurrentWeekToDateRange, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDateKeyFromValue, getDayDateKey, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getInitials, getKpiSignalLabel, getKpiSignalStatus, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getMonthlyTrendComparisonLabel, getNextUpdateInterval, getOperationalDate, getRoleAssignmentKind, getRoleDescription, getRoleLabel, getRoleMetadata, getRoleNavPaths, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShiftWorkDurationSeconds, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getVisibleRolesForCurrentUser, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isEfficiencyOnTrack, isFactoryScopedRole, isFullMonthRange, isIgnorableFrontendError, isLegacyConfiguration, isLoopbackHostname, isPrefetchError, isRealSku, isRecentFlowVideoGridMetricMode, isSafari, isSupervisorRole, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWipGatedVideoGridMetricMode, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, lineLeaderboardService, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeActionFamily, normalizeDateKeyRange, normalizeDateKeyRangeUnbounded, normalizeRoleLevel, normalizeVideoGridMetricMode, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, pickPreferredLineMetricsRow, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, productionPlanService, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSentryQuotaForTests, resetSubscriptionManager, resolveDefaultSkuId, resolveLiveSkuId, s3VideoPreloader, selectPreferredLineMetricsRow, setSentryUserContext, setSentryWorkspaceContext, shouldEnableLocalDevTestLogin, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useClipsInit, useCompanyClipsCost, useCompanyFastSlowClipFiltersEnabled, useCompanyHasVlmEnabledLine, useCompanyUsersUsage, useCompanyWorkspaceHourAiSummaryEnabled, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHideMobileHeader, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useIdleTimeVlmConfig, useKpiTrends, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMobileMenu, useMonthlyTrend, useMultiLineShiftConfigs, useNavigation, useOperationalShiftKey, useOptionalSupabase, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShiftGroups, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthLastSeen, useWorkspaceHealthStatus, useWorkspaceHourSummary, useWorkspaceLightTimeline, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, useWorkspaceVideoStreams, userService, videoPrefetchManager, videoPreloader, weeklyTopPerformerService, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
96114
+ export { ACTION_FAMILIES, ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AvatarUpload, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ChangeRoleDialog, ClipFilterProvider, ClipsCostView_default as ClipsCostView, CompactWorkspaceHealthCard, ConfirmRemoveUserDialog, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_HOME_VIEW_CONFIG, DEFAULT_MAP_VIEW_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_SHIFT_DATA, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, DiagnosisVideoModal, EFFICIENCY_ON_TRACK_THRESHOLD, EmptyStateMessage, EncouragementOverlay, FactoryAssignmentDropdown, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, FittingTitle, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthDateShiftSelector, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, HourlyUptimeChart, ISTTimer_default as ISTTimer, IdleTimeVlmConfigProvider, ImprovementCenterView_default as ImprovementCenterView, InlineEditableText, InteractiveOnboardingTour, InvitationService, InvitationsTable, InviteUserDialog, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPI_SIGNAL_LABELS, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend5 as Legend, LineAssignmentDropdown, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LineOvertakeNotificationManager, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, MobileMenuProvider, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlantHeadView_default as PlantHeadView, PlayPauseIndicator, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProductionPlanApiError, ProductionPlanView_default as ProductionPlanView, ProfileView_default as ProfileView, ROOT_DASHBOARD_EVENT_NAMES, RecentFlowSnapshotGrid, RegistryProvider, RoleBadge, S3ClipsSupabaseService as S3ClipsService, S3Service, SENTRY_HANDLED_EVENT_SESSION_LIMIT, SENTRY_HANDLED_EVENT_WINDOW_MS, SENTRY_QUOTA_STORAGE_KEY, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SessionTracker, SessionTrackingContext, SessionTrackingProvider, SettingsPopup, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UptimeDonutChart, UptimeLineChart, UptimeMetricCards, UserAvatar, UserManagementService, UserManagementTable, UserService, UserUsageDetailModal, UserUsageStats, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceCycleTimeMetricCards, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, addSentryBreadcrumb, aggregateKPIsFromLineMetricsRows, aggregateLineSignals, alertsService, apiUtils, areAllLinesOnSameShift, authCoreService, authOTPService, authRateLimitService, awardsService, buildDateKey, buildKPIsFromLineMetricsRow, buildKpiLineHierarchy, buildLegacyLineOvertakeEventKey, buildLineLeaderboardRows, buildLineOvertakeEventKey, buildLineSkuBreakdown, buildShiftGroupsKey, canPermissionEditProductionPlan, canRoleAccessDashboardPath, canRoleAccessTeamManagement, canRoleAssignFactories, canRoleAssignLines, canRoleChangeRole, canRoleEditProductionPlan, canRoleInviteRole, canRoleManageCompany, canRoleManageTargets, canRoleManageUsers, canRoleRemoveUser, canRoleViewClipsCost, canRoleViewUsageStats, captureHandledFrontendException, captureSentryException, captureSentryMessage, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearSentryContext, clearWorkspaceDisplayNamesCache, cn, combineLineMetricsRows, countRealSkus, createDefaultKPIs, createInvitationService, createLinesService, createSessionTracker, createStorageService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, detectLineOvertakeEvents, fetchIdleTimeReasons, fetchLineDummySkuId, fetchLineSkuCatalog, filterDataByDateKeyRange, filterRealSkuBreakdown, forceRefreshWorkspaceDisplayNames, formatAwardMonth, formatDateInZone, formatDateKeyForDisplay, formatDateTimeInZone, formatDuration2 as formatDuration, formatISTDate, formatIdleTime, formatRangeLabel, formatReasonLabel, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getActionDisplayName, getActiveShift, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAllWorkspaceDisplayNamesSnapshot, getAnonClient, getAssignableRoles, getAssignmentColumnLabel, getAvailableShiftIds, getAwardBadgeType, getAwardDescription, getAwardTitle, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentShiftForLine, getCurrentTimeInZone, getCurrentWeekFullRange, getCurrentWeekToDateRange, getDashboardHeaderTimeInZone, getDateKeyFromDate, getDateKeyFromValue, getDayDateKey, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getInitials, getKpiSignalLabel, getKpiSignalStatus, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getMonthKeyBounds, getMonthWeekRanges, getMonthlyTrendComparisonLabel, getNextUpdateInterval, getOperationalDate, getRoleAssignmentKind, getRoleDescription, getRoleLabel, getRoleMetadata, getRoleNavPaths, getS3SignedUrl, getS3VideoSrc, getShiftData, getShiftNameById, getShiftWorkDurationSeconds, getShortShiftName, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUniformShiftGroup, getUserThreads, getUserThreadsPaginated, getVisibleRolesForCurrentUser, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, groupLinesByShift, hasAnyShiftData, identifyCoreUser, initializeCoreMixpanel, isEfficiencyOnTrack, isFactoryScopedRole, isFullMonthRange, isIgnorableFrontendError, isLegacyConfiguration, isLoopbackHostname, isPrefetchError, isRealSku, isRecentFlowVideoGridMetricMode, isSafari, isSupervisorRole, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWipGatedVideoGridMetricMode, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, lineLeaderboardService, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, normalizeActionFamily, normalizeDateKeyRange, normalizeDateKeyRangeUnbounded, normalizeRoleLevel, normalizeVideoGridMetricMode, optifyeAgentClient, parseDateKeyToDate, parseS3Uri, pickPreferredLineMetricsRow, preInitializeWorkspaceDisplayNames, preInitializeWorkspaceDisplayNamesForLines, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, productionPlanService, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSentryQuotaForTests, resetSubscriptionManager, resolveDefaultSkuId, resolveLiveSkuId, s3VideoPreloader, selectPreferredLineMetricsRow, setSentryUserContext, setSentryWorkspaceContext, shouldEnableLocalDevTestLogin, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, subscribeWorkspaceDisplayNames, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, transformToChartData, updateThreadTitle, upsertWorkspaceDisplayNameInCache, useAccessControl, useActiveBreaks, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useClipsInit, useCompanyClipsCost, useCompanyFastSlowClipFiltersEnabled, useCompanyHasVlmEnabledLine, useCompanyUsersUsage, useCompanyWorkspaceHourAiSummaryEnabled, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHideMobileHeader, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useIdleTimeClipClassifications, useIdleTimeReasons, useIdleTimeVlmConfig, useKpiTrends, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useLines, useMessages, useMetrics, useMobileMenu, useMonthlyTrend, useMultiLineShiftConfigs, useNavigation, useOperationalShiftKey, useOptionalSupabase, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useSessionTracking, useSessionTrackingContext, useShiftConfig, useShiftGroups, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useSupervisorsByLineIds, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useUserUsage, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthLastSeen, useWorkspaceHealthStatus, useWorkspaceHourSummary, useWorkspaceLightTimeline, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, useWorkspaceUptimeTimeline, useWorkspaceVideoStreams, userService, videoPrefetchManager, videoPreloader, weeklyTopPerformerService, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
96115
+ //# sourceMappingURL=index.mjs.map
96116
+ //# sourceMappingURL=index.mjs.map