@optifye/dashboard-core 6.12.51 → 6.12.52

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
+ }
12230
12530
  }
12231
12531
  }
12232
- async sendEndSession(reason) {
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;
12542
+ }
12543
+ }
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,
@@ -17435,7 +17763,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
17435
17763
  return Number.isFinite(parsed) ? parsed : DEFAULT_MAX_MANIFEST_AGE_MS;
17436
17764
  })();
17437
17765
  const manifestStaleThresholdMs = maxManifestAgeMs > 0 ? Math.min(maxManifestAgeMs, SEGMENT_MAX_AGE_MS) : SEGMENT_MAX_AGE_MS;
17438
- const debugLog = (...args) => {
17766
+ const debugLog2 = (...args) => {
17439
17767
  if (debugEnabled) {
17440
17768
  console.log(...args);
17441
17769
  }
@@ -17617,7 +17945,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
17617
17945
  return;
17618
17946
  }
17619
17947
  } catch (error) {
17620
- debugLog("[HLS] Stale manifest poll failed", error);
17948
+ debugLog2("[HLS] Stale manifest poll failed", error);
17621
17949
  }
17622
17950
  staleManifestPollDelayRef.current = Math.min(
17623
17951
  staleManifestPollDelayRef.current + 5e3,
@@ -17967,7 +18295,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
17967
18295
  }
17968
18296
  return true;
17969
18297
  } catch (error) {
17970
- debugLog("[HLS] Manifest freshness check failed", error);
18298
+ debugLog2("[HLS] Manifest freshness check failed", error);
17971
18299
  {
17972
18300
  markStaleStream("manifest freshness check failed");
17973
18301
  return false;
@@ -18013,7 +18341,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18013
18341
  };
18014
18342
  const softRestart = (reason) => {
18015
18343
  if (staleManifestTriggeredRef.current) {
18016
- debugLog("[HLS] Skip soft restart while manifest is stale", reason);
18344
+ debugLog2("[HLS] Skip soft restart while manifest is stale", reason);
18017
18345
  return;
18018
18346
  }
18019
18347
  console.warn(`[HLS] Soft restart: ${reason}`);
@@ -18046,7 +18374,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18046
18374
  };
18047
18375
  const hardRestart = (reason) => {
18048
18376
  if (staleManifestTriggeredRef.current) {
18049
- debugLog("[HLS] Skip hard restart while manifest is stale", reason);
18377
+ debugLog2("[HLS] Skip hard restart while manifest is stale", reason);
18050
18378
  return;
18051
18379
  }
18052
18380
  console.warn(`[HLS] Hard restart: ${reason}`);
@@ -18300,7 +18628,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18300
18628
  hls.loadSource(resolvedHlsSrc);
18301
18629
  activeStreamUrlRef.current = resolvedHlsSrc;
18302
18630
  hls.on(Hls.Events.ERROR, (_, data) => {
18303
- debugLog("[HLS] Error", {
18631
+ debugLog2("[HLS] Error", {
18304
18632
  type: data.type,
18305
18633
  details: data.details,
18306
18634
  fatal: data.fatal,
@@ -18308,7 +18636,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18308
18636
  frag: data.frag?.sn
18309
18637
  });
18310
18638
  if (data.type === Hls.ErrorTypes.MEDIA_ERROR && data.details === Hls.ErrorDetails.BUFFER_STALLED_ERROR) {
18311
- debugLog("[HLS] Buffer stalled, waiting for next segment");
18639
+ debugLog2("[HLS] Buffer stalled, waiting for next segment");
18312
18640
  attemptPlay();
18313
18641
  return;
18314
18642
  }
@@ -18452,7 +18780,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
18452
18780
  }
18453
18781
  }
18454
18782
  }
18455
- debugLog("[HLS] Level loaded", {
18783
+ debugLog2("[HLS] Level loaded", {
18456
18784
  targetduration: details.targetduration,
18457
18785
  edge: details.edge,
18458
18786
  fragments: data.details?.fragments?.length
@@ -18848,6 +19176,7 @@ var isInitializing = false;
18848
19176
  var initializedWithLineIds = [];
18849
19177
  var missingLineContextWarnings = /* @__PURE__ */ new Set();
18850
19178
  var lineLoadPromises = /* @__PURE__ */ new Map();
19179
+ var GLOBAL_CACHE_KEY = "global";
18851
19180
  var initializationPromise = null;
18852
19181
  var workspaceDisplayNamesListeners = /* @__PURE__ */ new Set();
18853
19182
  var notifyWorkspaceDisplayNamesListeners = (changedLineId) => {
@@ -18869,6 +19198,22 @@ var storeLineDisplayNames = (lineId, lineDisplayNamesMap) => {
18869
19198
  runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
18870
19199
  });
18871
19200
  };
19201
+ var storeGlobalDisplayNames = (displayNamesMap) => {
19202
+ runtimeWorkspaceDisplayNames[GLOBAL_CACHE_KEY] = {};
19203
+ displayNamesMap.forEach((displayName, workspaceId) => {
19204
+ runtimeWorkspaceDisplayNames[GLOBAL_CACHE_KEY][workspaceId] = displayName;
19205
+ });
19206
+ };
19207
+ var storeDisplayNamesByLine = (displayNamesByLine, requestedLineIds = []) => {
19208
+ displayNamesByLine.forEach((lineDisplayNamesMap, lineId) => {
19209
+ storeLineDisplayNames(lineId, lineDisplayNamesMap);
19210
+ });
19211
+ requestedLineIds.forEach((lineId) => {
19212
+ if (!runtimeWorkspaceDisplayNames[lineId]) {
19213
+ runtimeWorkspaceDisplayNames[lineId] = {};
19214
+ }
19215
+ });
19216
+ };
18872
19217
  var ensureLineWorkspaceDisplayNamesLoaded = async (lineId) => {
18873
19218
  if (!lineId || runtimeWorkspaceDisplayNames[lineId]) {
18874
19219
  return;
@@ -18952,7 +19297,11 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
18952
19297
  console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
18953
19298
  runtimeWorkspaceDisplayNames = {};
18954
19299
  lineLoadPromises.clear();
18955
- if (targetLineIds.length > 0) {
19300
+ if (targetLineIds.length > 1) {
19301
+ const displayNamesByLine = await workspaceService.getWorkspaceDisplayNamesByLine(void 0, targetLineIds);
19302
+ storeDisplayNamesByLine(displayNamesByLine, targetLineIds);
19303
+ console.log(`\u2705 Stored display names for ${displayNamesByLine.size} authorized lines`);
19304
+ } else if (targetLineIds.length === 1) {
18956
19305
  const results = await Promise.all(
18957
19306
  targetLineIds.map(async (lineId) => {
18958
19307
  console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
@@ -18967,10 +19316,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
18967
19316
  } else {
18968
19317
  console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
18969
19318
  const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
18970
- runtimeWorkspaceDisplayNames["global"] = {};
18971
- allWorkspacesMap.forEach((displayName, workspaceId) => {
18972
- runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
18973
- });
19319
+ storeGlobalDisplayNames(allWorkspacesMap);
18974
19320
  }
18975
19321
  isInitialized = true;
18976
19322
  initializedWithLineIds = targetLineIds;
@@ -18986,6 +19332,37 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
18986
19332
  })();
18987
19333
  await initializationPromise;
18988
19334
  }
19335
+ var preInitializeWorkspaceDisplayNamesForLines = async (lineIds) => {
19336
+ const uniqueLineIds = Array.from(new Set((lineIds || []).filter(Boolean)));
19337
+ if (uniqueLineIds.length === 0) {
19338
+ await preInitializeWorkspaceDisplayNames();
19339
+ return;
19340
+ }
19341
+ if (uniqueLineIds.length === 1) {
19342
+ await preInitializeWorkspaceDisplayNames(uniqueLineIds[0]);
19343
+ return;
19344
+ }
19345
+ if (isInitialized && uniqueLineIds.every((lineId) => Boolean(runtimeWorkspaceDisplayNames[lineId]))) {
19346
+ return;
19347
+ }
19348
+ if (initializationPromise) {
19349
+ await initializationPromise;
19350
+ if (uniqueLineIds.every((lineId) => Boolean(runtimeWorkspaceDisplayNames[lineId]))) {
19351
+ return;
19352
+ }
19353
+ }
19354
+ isInitializing = true;
19355
+ initializationPromise = workspaceService.getWorkspaceDisplayNamesByLine(void 0, uniqueLineIds).then((displayNamesByLine) => {
19356
+ storeDisplayNamesByLine(displayNamesByLine, uniqueLineIds);
19357
+ initializedWithLineIds = uniqueLineIds;
19358
+ isInitialized = true;
19359
+ notifyWorkspaceDisplayNamesListeners();
19360
+ }).finally(() => {
19361
+ isInitializing = false;
19362
+ initializationPromise = null;
19363
+ });
19364
+ await initializationPromise;
19365
+ };
18989
19366
  var preInitializeWorkspaceDisplayNames = async (lineId) => {
18990
19367
  console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
18991
19368
  if (isInitialized) {
@@ -38720,6 +39097,7 @@ var HourlyOutputChartComponent = ({
38720
39097
  timeRange: data2.timeRange,
38721
39098
  startTime,
38722
39099
  endTime,
39100
+ timezone,
38723
39101
  output: Math.round(data2.originalOutput || 0),
38724
39102
  target: data2.target,
38725
39103
  status: data2.status
@@ -38893,6 +39271,7 @@ var HourlyOutputChartComponent = ({
38893
39271
  timeRange: entry.timeRange,
38894
39272
  startTime,
38895
39273
  endTime,
39274
+ timezone,
38896
39275
  output: Math.round(entry.originalOutput || 0),
38897
39276
  target: entry.target,
38898
39277
  status: entry.status
@@ -42360,6 +42739,65 @@ var buildPrefetchedExplorerMetadata = (activeFilter, metadataCategoryId, categor
42360
42739
  };
42361
42740
  };
42362
42741
  var shouldDeferClipPlayerRender = (cropLoading, workspaceCrop) => cropLoading && workspaceCrop === null;
42742
+ var parseSortableCycleTime = (value) => {
42743
+ if (typeof value === "number" && Number.isFinite(value)) {
42744
+ return value;
42745
+ }
42746
+ if (typeof value === "string") {
42747
+ const parsed = Number(value);
42748
+ return Number.isFinite(parsed) ? parsed : null;
42749
+ }
42750
+ return null;
42751
+ };
42752
+ var readSortableCycleTime = (clip) => {
42753
+ const candidate = clip;
42754
+ return parseSortableCycleTime(candidate?.cycleTimeSeconds) ?? parseSortableCycleTime(candidate?.cycle_time_seconds) ?? parseSortableCycleTime(candidate?.duration) ?? parseSortableCycleTime(
42755
+ candidate?.original_task_metadata?.cycle_time
42756
+ ) ?? null;
42757
+ };
42758
+ var readSortableTimestamp = (clip) => {
42759
+ const candidate = clip;
42760
+ const timestamp = candidate?.creation_timestamp || candidate?.timestamp || candidate?.clip_timestamp || candidate?.clip_end_time || candidate?.clip_start_time;
42761
+ if (typeof timestamp !== "string") {
42762
+ return 0;
42763
+ }
42764
+ const parsed = new Date(timestamp).getTime();
42765
+ return Number.isFinite(parsed) ? parsed : 0;
42766
+ };
42767
+ var sortPercentileCycleClipsForDisplay = (categoryId, clips) => {
42768
+ if (categoryId !== "fast-cycles" && categoryId !== "slow-cycles") {
42769
+ return [...clips];
42770
+ }
42771
+ return [...clips].sort((left, right) => {
42772
+ const leftCycleTime = readSortableCycleTime(left);
42773
+ const rightCycleTime = readSortableCycleTime(right);
42774
+ const leftMissingCycleTime = leftCycleTime === null;
42775
+ const rightMissingCycleTime = rightCycleTime === null;
42776
+ if (leftMissingCycleTime !== rightMissingCycleTime) {
42777
+ return leftMissingCycleTime ? 1 : -1;
42778
+ }
42779
+ if (leftCycleTime !== null && rightCycleTime !== null && leftCycleTime !== rightCycleTime) {
42780
+ return categoryId === "fast-cycles" ? leftCycleTime - rightCycleTime : rightCycleTime - leftCycleTime;
42781
+ }
42782
+ return readSortableTimestamp(right) - readSortableTimestamp(left);
42783
+ });
42784
+ };
42785
+ var resolveInitialClipCategory = (categoryCandidates, clipTypes = [], counts = {}) => {
42786
+ const candidates = Array.from(
42787
+ new Set(
42788
+ categoryCandidates.filter((candidate) => Boolean(candidate)).map((candidate) => candidate.trim()).filter(Boolean)
42789
+ )
42790
+ );
42791
+ if (candidates.length === 0) {
42792
+ return null;
42793
+ }
42794
+ const availableCategories = /* @__PURE__ */ new Set();
42795
+ clipTypes.forEach((type) => {
42796
+ if (type?.type) availableCategories.add(String(type.type));
42797
+ if (type?.id) availableCategories.add(String(type.id));
42798
+ });
42799
+ return candidates.find((candidate) => (counts[candidate] || 0) > 0) || candidates.find((candidate) => availableCategories.has(candidate)) || null;
42800
+ };
42363
42801
  var getCategoryMetadataLoadPlanForFilterChange = ({
42364
42802
  activeFilter,
42365
42803
  currentClipId,
@@ -42371,6 +42809,12 @@ var getCategoryMetadataLoadPlanForFilterChange = ({
42371
42809
  if (activeFilter === "recent_flow_red_streak" && categoryTotal > 0) {
42372
42810
  return { shouldLoad: true, autoLoadFirstVideo: true };
42373
42811
  }
42812
+ if (activeFilter === "fast-cycles" || activeFilter === "slow-cycles") {
42813
+ return { shouldLoad: true, autoLoadFirstVideo: true };
42814
+ }
42815
+ if (categoryTotal > 0 && activeFilter === "cycle_completion") {
42816
+ return { shouldLoad: true, autoLoadFirstVideo: true };
42817
+ }
42374
42818
  return {
42375
42819
  shouldLoad: Boolean(currentClipId),
42376
42820
  autoLoadFirstVideo: false
@@ -46208,6 +46652,36 @@ var CLIP_METADATA_PAGE_SIZE = 50;
46208
46652
  var RECENT_FLOW_RED_STREAK_CLIP_TYPE2 = "recent_flow_red_streak";
46209
46653
  var RECENT_FLOW_RED_STREAK_DISPLAY_LABEL = "Low moments";
46210
46654
  var RECENT_FLOW_RED_STREAK_DISPLAY_SUBTITLE = "Moments of low efficiency";
46655
+ var REQUIRED_HOURLY_HANDOFF_CATEGORIES = {
46656
+ cycle_completion: {
46657
+ id: "cycle_completion",
46658
+ label: "Cycle Completion",
46659
+ description: "Successfully completed production cycles",
46660
+ color: "green",
46661
+ icon: "check-circle"
46662
+ },
46663
+ idle_time: {
46664
+ id: "idle_time",
46665
+ label: "Idle Time",
46666
+ description: "Idle periods",
46667
+ color: "purple",
46668
+ icon: "clock"
46669
+ },
46670
+ [RECENT_FLOW_RED_STREAK_CLIP_TYPE2]: {
46671
+ id: RECENT_FLOW_RED_STREAK_CLIP_TYPE2,
46672
+ label: RECENT_FLOW_RED_STREAK_DISPLAY_LABEL,
46673
+ description: RECENT_FLOW_RED_STREAK_DISPLAY_SUBTITLE,
46674
+ color: "red",
46675
+ icon: "alert-triangle"
46676
+ }
46677
+ };
46678
+ var REQUIRED_HOURLY_FILTER_CATEGORY_IDS = [
46679
+ RECENT_FLOW_RED_STREAK_CLIP_TYPE2,
46680
+ "cycle_completion",
46681
+ "fast-cycles",
46682
+ "slow-cycles",
46683
+ "idle_time"
46684
+ ];
46211
46685
  var parseCycleTime = (value) => {
46212
46686
  if (typeof value === "number" && Number.isFinite(value)) {
46213
46687
  return value;
@@ -46236,6 +46710,21 @@ var formatDurationLabel = (seconds) => {
46236
46710
  }
46237
46711
  return `${Math.round(roundedSeconds / 60)} min`;
46238
46712
  };
46713
+ var timeValueToMinutes = (value) => {
46714
+ const [hourValue, minuteValue] = value.substring(0, 5).split(":").map(Number);
46715
+ if (!Number.isInteger(hourValue) || !Number.isInteger(minuteValue) || hourValue < 0 || hourValue > 23 || minuteValue < 0 || minuteValue > 59) {
46716
+ return null;
46717
+ }
46718
+ return hourValue * 60 + minuteValue;
46719
+ };
46720
+ var isMinuteInTimeWindow = (minute, startValue, endValue) => {
46721
+ const startMinute = timeValueToMinutes(startValue);
46722
+ const endMinute = timeValueToMinutes(endValue);
46723
+ if (startMinute === null || endMinute === null) {
46724
+ return true;
46725
+ }
46726
+ return endMinute > startMinute ? minute >= startMinute && minute < endMinute : minute >= startMinute || minute < endMinute;
46727
+ };
46239
46728
  var sortRedFlowMetadata = (clips) => {
46240
46729
  return clips.slice().sort((left, right) => {
46241
46730
  const getOutputShortfall = (clip) => {
@@ -46260,6 +46749,27 @@ var sortRedFlowMetadata = (clips) => {
46260
46749
  return (Number.isFinite(rightTime) ? rightTime : 0) - (Number.isFinite(leftTime) ? leftTime : 0);
46261
46750
  });
46262
46751
  };
46752
+ var buildClipMetadataFromVideo = (video, index) => ({
46753
+ id: video.id,
46754
+ clipId: video.id,
46755
+ clip_timestamp: video.creation_timestamp || video.timestamp,
46756
+ description: video.description,
46757
+ severity: video.severity,
46758
+ category: video.type,
46759
+ duration: typeof video.duration === "number" ? video.duration : typeof video.cycle_time_seconds === "number" ? video.cycle_time_seconds : void 0,
46760
+ clip_start_time: video.clip_start_time,
46761
+ clip_end_time: video.clip_end_time,
46762
+ index,
46763
+ idle_start_time: video.idle_start_time,
46764
+ idle_end_time: video.idle_end_time,
46765
+ cycle_item_count: null,
46766
+ red_flow_timeline: video.red_flow_timeline,
46767
+ red_flow_severity_score: video.red_flow_severity_score,
46768
+ red_flow_output_shortfall_units: video.red_flow_output_shortfall_units,
46769
+ red_flow_worst_minute: video.red_flow_worst_minute,
46770
+ red_flow_explanation_summary: video.red_flow_explanation_summary,
46771
+ red_flow_explanation: video.red_flow_explanation
46772
+ });
46263
46773
  var getSeverityIcon = (severity, categoryId, cycleTimeSeconds, targetCycleTime, clipId) => {
46264
46774
  if (categoryId === "idle_time" || categoryId === "low_value" || categoryId === "longest-idles") {
46265
46775
  return null;
@@ -46338,6 +46848,8 @@ var FileManagerFilters = ({
46338
46848
  idleTimeVlmEnabled = false,
46339
46849
  showPercentileCycleFilters = true,
46340
46850
  prefetchedClipMetadata,
46851
+ prefetchedClipTotals,
46852
+ prefetchedPercentileClips,
46341
46853
  externallyManagedLoadingCategories,
46342
46854
  activeCategoryLoading,
46343
46855
  idleClipSort = "latest",
@@ -46345,6 +46857,7 @@ var FileManagerFilters = ({
46345
46857
  initialTimeFilter
46346
46858
  }) => {
46347
46859
  const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set());
46860
+ const [activeInitialTimeFilter, setActiveInitialTimeFilter] = useState(initialTimeFilter ?? null);
46348
46861
  const [startTime, setStartTime] = useState(initialTimeFilter?.startTime ?? "");
46349
46862
  const [endTime, setEndTime] = useState(initialTimeFilter?.endTime ?? "");
46350
46863
  const [isTimeFilterActive, setIsTimeFilterActive] = useState(
@@ -46359,14 +46872,93 @@ var FileManagerFilters = ({
46359
46872
  const [showIdleLabelFilterModal, setShowIdleLabelFilterModal] = useState(false);
46360
46873
  const [isLoadingIdleReasonOptions, setIsLoadingIdleReasonOptions] = useState(false);
46361
46874
  const timezone = useAppTimezone();
46875
+ const activeTimeFilterTimezone = activeInitialTimeFilter?.timezone || initialTimeFilter?.timezone || timezone;
46876
+ const activeTimeFilterKey = useMemo(() => startTime && endTime && isTimeFilterActive ? `${startTime}-${endTime}-${activeTimeFilterTimezone}` : "none", [activeTimeFilterTimezone, endTime, isTimeFilterActive, startTime]);
46877
+ const initialTimeFilterCategoryIds = useMemo(() => {
46878
+ if (!activeInitialTimeFilter) {
46879
+ return [];
46880
+ }
46881
+ return Array.from(new Set([
46882
+ activeInitialTimeFilter.categoryId,
46883
+ ...activeInitialTimeFilter.categoryIds || []
46884
+ ].filter((value) => typeof value === "string" && value.length > 0)));
46885
+ }, [activeInitialTimeFilter]);
46886
+ const requiredHourlyFilterCategoryIds = useMemo(() => {
46887
+ if (initialTimeFilterCategoryIds.length > 0) {
46888
+ return initialTimeFilterCategoryIds;
46889
+ }
46890
+ if (!isTimeFilterActive || !startTime || !endTime) {
46891
+ return [];
46892
+ }
46893
+ return REQUIRED_HOURLY_FILTER_CATEGORY_IDS;
46894
+ }, [endTime, initialTimeFilterCategoryIds, isTimeFilterActive, startTime]);
46895
+ const categoriesForTree = useMemo(() => {
46896
+ if (requiredHourlyFilterCategoryIds.length === 0) {
46897
+ return categories;
46898
+ }
46899
+ const existingCategoryIds = new Set(categories.map((category) => category.id));
46900
+ const mergedCategories = [...categories];
46901
+ requiredHourlyFilterCategoryIds.forEach((categoryId) => {
46902
+ const fallbackCategory = REQUIRED_HOURLY_HANDOFF_CATEGORIES[categoryId];
46903
+ if (!fallbackCategory || existingCategoryIds.has(categoryId)) {
46904
+ return;
46905
+ }
46906
+ mergedCategories.push(fallbackCategory);
46907
+ existingCategoryIds.add(categoryId);
46908
+ });
46909
+ return mergedCategories;
46910
+ }, [categories, requiredHourlyFilterCategoryIds]);
46362
46911
  const supabase = useSupabase();
46363
46912
  const [clipMetadata, setClipMetadata] = useState({});
46913
+ const [scopedCategoryTotals, setScopedCategoryTotals] = useState({});
46364
46914
  const [loadingCategories, setLoadingCategories] = useState(/* @__PURE__ */ new Set());
46915
+ const isCategoryScopedByTimeFilter = useCallback((categoryId) => {
46916
+ if (!startTime || !endTime || !isTimeFilterActive) {
46917
+ return false;
46918
+ }
46919
+ if (!activeInitialTimeFilter) {
46920
+ return true;
46921
+ }
46922
+ if (activeFilter === categoryId || activeInitialTimeFilter.categoryId === categoryId) {
46923
+ return true;
46924
+ }
46925
+ return Boolean(activeInitialTimeFilter.categoryIds?.includes(categoryId));
46926
+ }, [
46927
+ activeFilter,
46928
+ activeInitialTimeFilter,
46929
+ endTime,
46930
+ isTimeFilterActive,
46931
+ startTime
46932
+ ]);
46933
+ const isRequiredHourlyFilterCategory = useCallback((categoryId) => requiredHourlyFilterCategoryIds.includes(categoryId), [requiredHourlyFilterCategoryIds]);
46934
+ const manualHourlySnapshotKey = useMemo(() => {
46935
+ if (activeInitialTimeFilter || !isTimeFilterActive || !startTime || !endTime || !workspaceId || !date || shift === void 0) {
46936
+ return null;
46937
+ }
46938
+ return `${workspaceId}:${date}:${shift}:${startTime}:${endTime}:${activeTimeFilterTimezone}`;
46939
+ }, [
46940
+ activeInitialTimeFilter,
46941
+ activeTimeFilterTimezone,
46942
+ date,
46943
+ endTime,
46944
+ isTimeFilterActive,
46945
+ shift,
46946
+ startTime,
46947
+ workspaceId
46948
+ ]);
46949
+ const manualHourlySnapshotAppliedKeyRef = useRef(null);
46950
+ const manualHourlySnapshotInFlightKeyRef = useRef(null);
46951
+ const [manualHourlySnapshotFailureKey, setManualHourlySnapshotFailureKey] = useState(null);
46952
+ const isManualHourlySnapshotPending = Boolean(
46953
+ manualHourlySnapshotKey && manualHourlySnapshotAppliedKeyRef.current !== manualHourlySnapshotKey && manualHourlySnapshotFailureKey !== manualHourlySnapshotKey
46954
+ );
46365
46955
  const [categoryPages, setCategoryPages] = useState({});
46366
46956
  const [categoryHasMore, setCategoryHasMore] = useState({});
46367
46957
  const [localClipClassifications, setLocalClipClassifications] = useState({});
46368
46958
  const clipMetadataRef = useRef({});
46369
46959
  const inFlightMetadataRequestsRef = useRef(/* @__PURE__ */ new Set());
46960
+ const inFlightPercentileClipRequestsRef = useRef(/* @__PURE__ */ new Set());
46961
+ const autoSelectedScopedClipRef = useRef(null);
46370
46962
  const previousIdleClipSortRef = useRef(idleClipSort);
46371
46963
  const mergedClipClassifications = useMemo(() => ({
46372
46964
  ...clipClassifications || {},
@@ -46379,12 +46971,14 @@ var FileManagerFilters = ({
46379
46971
  if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime) {
46380
46972
  return;
46381
46973
  }
46974
+ setActiveInitialTimeFilter(initialTimeFilter);
46382
46975
  setStartTime(initialTimeFilter.startTime);
46383
46976
  setEndTime(initialTimeFilter.endTime);
46384
46977
  setIsTimeFilterActive(true);
46385
46978
  setShowTimeFilterModal(false);
46386
46979
  setStartSearchTerm("");
46387
46980
  setEndSearchTerm("");
46981
+ setScopedCategoryTotals({});
46388
46982
  }, [initialTimeFilter?.startTime, initialTimeFilter?.endTime]);
46389
46983
  useEffect(() => {
46390
46984
  if (previousIdleClipSortRef.current === idleClipSort) {
@@ -46416,18 +47010,66 @@ var FileManagerFilters = ({
46416
47010
  return next;
46417
47011
  });
46418
47012
  }, [idleClipSort]);
47013
+ useEffect(() => {
47014
+ manualHourlySnapshotAppliedKeyRef.current = null;
47015
+ manualHourlySnapshotInFlightKeyRef.current = null;
47016
+ setManualHourlySnapshotFailureKey(null);
47017
+ setScopedCategoryTotals({});
47018
+ setClipMetadata({});
47019
+ setCategoryPages({});
47020
+ setCategoryHasMore({});
47021
+ setPercentileClips({});
47022
+ setPercentileCounts({
47023
+ "fast-cycles": null,
47024
+ "slow-cycles": null
47025
+ });
47026
+ }, [activeTimeFilterKey]);
46419
47027
  const isCategoryExternallyManaged = useCallback((categoryId) => {
46420
47028
  if (!categoryId) {
46421
47029
  return false;
46422
47030
  }
46423
- if (prefetchedClipMetadata && Array.isArray(prefetchedClipMetadata[categoryId]) && prefetchedClipMetadata[categoryId].length > 0) {
47031
+ if (isManualHourlySnapshotPending && requiredHourlyFilterCategoryIds.includes(categoryId)) {
47032
+ return true;
47033
+ }
47034
+ if (prefetchedClipMetadata && Array.isArray(prefetchedClipMetadata[categoryId]) && (prefetchedClipMetadata[categoryId].length > 0 || typeof prefetchedClipTotals?.[categoryId] === "number")) {
47035
+ return true;
47036
+ }
47037
+ if (prefetchedPercentileClips && Array.isArray(prefetchedPercentileClips[categoryId]) && (prefetchedPercentileClips[categoryId].length > 0 || isCategoryScopedByTimeFilter(categoryId) && typeof prefetchedClipTotals?.[categoryId] === "number")) {
46424
47038
  return true;
46425
47039
  }
46426
47040
  if (externallyManagedLoadingCategories?.[categoryId]) {
46427
47041
  return true;
46428
47042
  }
46429
47043
  return false;
46430
- }, [prefetchedClipMetadata, externallyManagedLoadingCategories]);
47044
+ }, [
47045
+ externallyManagedLoadingCategories,
47046
+ isManualHourlySnapshotPending,
47047
+ isCategoryScopedByTimeFilter,
47048
+ prefetchedClipMetadata,
47049
+ prefetchedClipTotals,
47050
+ prefetchedPercentileClips,
47051
+ requiredHourlyFilterCategoryIds
47052
+ ]);
47053
+ const getAvailableClipMetadata = useCallback((categoryId) => {
47054
+ const localClips = clipMetadata[categoryId];
47055
+ if (Array.isArray(localClips) && localClips.length > 0) {
47056
+ return localClips;
47057
+ }
47058
+ const prefetchedClips = prefetchedClipMetadata?.[categoryId];
47059
+ if (Array.isArray(prefetchedClips) && prefetchedClips.length > 0) {
47060
+ return prefetchedClips;
47061
+ }
47062
+ return [];
47063
+ }, [clipMetadata, prefetchedClipMetadata]);
47064
+ const hasKnownClipMetadata = useCallback((categoryId) => {
47065
+ if (Array.isArray(clipMetadata[categoryId])) {
47066
+ return true;
47067
+ }
47068
+ if (Array.isArray(prefetchedClipMetadata?.[categoryId])) {
47069
+ return true;
47070
+ }
47071
+ return typeof prefetchedClipTotals?.[categoryId] === "number";
47072
+ }, [clipMetadata, prefetchedClipMetadata, prefetchedClipTotals]);
46431
47073
  useEffect(() => {
46432
47074
  if (!prefetchedClipMetadata) {
46433
47075
  return;
@@ -46450,6 +47092,22 @@ var FileManagerFilters = ({
46450
47092
  });
46451
47093
  return changed ? next : prev;
46452
47094
  });
47095
+ setScopedCategoryTotals((prev) => {
47096
+ let changed = false;
47097
+ const next = { ...prev };
47098
+ Object.entries(prefetchedClipMetadata).forEach(([categoryId, clips]) => {
47099
+ if (!Array.isArray(clips) || !isCategoryScopedByTimeFilter(categoryId)) {
47100
+ return;
47101
+ }
47102
+ const nextTotal = typeof prefetchedClipTotals?.[categoryId] === "number" ? Math.max(0, prefetchedClipTotals[categoryId]) : clips.length;
47103
+ if (next[categoryId] === nextTotal) {
47104
+ return;
47105
+ }
47106
+ next[categoryId] = nextTotal;
47107
+ changed = true;
47108
+ });
47109
+ return changed ? next : prev;
47110
+ });
46453
47111
  setCategoryPages((prev) => {
46454
47112
  let changed = false;
46455
47113
  const next = { ...prev };
@@ -46474,7 +47132,7 @@ var FileManagerFilters = ({
46474
47132
  return;
46475
47133
  }
46476
47134
  const knownTotal = typeof counts?.[categoryId] === "number" ? counts[categoryId] : null;
46477
- const inferredHasMore = knownTotal !== null ? clips.length < knownTotal : prev[categoryId];
47135
+ const inferredHasMore = isCategoryScopedByTimeFilter(categoryId) ? false : knownTotal !== null ? clips.length < knownTotal : prev[categoryId];
46478
47136
  if (typeof inferredHasMore !== "boolean" || next[categoryId] === inferredHasMore) {
46479
47137
  return;
46480
47138
  }
@@ -46483,8 +47141,68 @@ var FileManagerFilters = ({
46483
47141
  });
46484
47142
  return changed ? next : prev;
46485
47143
  });
46486
- }, [prefetchedClipMetadata, counts]);
47144
+ }, [prefetchedClipMetadata, prefetchedClipTotals, counts, isCategoryScopedByTimeFilter]);
47145
+ useEffect(() => {
47146
+ if (!prefetchedPercentileClips) {
47147
+ return;
47148
+ }
47149
+ setPercentileClips((prev) => {
47150
+ let changed = false;
47151
+ const next = { ...prev };
47152
+ ["fast-cycles", "slow-cycles"].forEach((categoryId) => {
47153
+ const clips = prefetchedPercentileClips[categoryId];
47154
+ if (!Array.isArray(clips)) {
47155
+ return;
47156
+ }
47157
+ const previousClips = prev[categoryId] || [];
47158
+ const previousSignature = previousClips.map((clip) => clip.id).join("|");
47159
+ const nextSignature = clips.map((clip) => clip.id).join("|");
47160
+ if (previousSignature === nextSignature) {
47161
+ return;
47162
+ }
47163
+ next[categoryId] = clips;
47164
+ changed = true;
47165
+ });
47166
+ return changed ? next : prev;
47167
+ });
47168
+ setPercentileCounts((prev) => {
47169
+ let changed = false;
47170
+ const next = { ...prev };
47171
+ ["fast-cycles", "slow-cycles"].forEach((categoryId) => {
47172
+ const clips = prefetchedPercentileClips[categoryId];
47173
+ if (!Array.isArray(clips)) {
47174
+ return;
47175
+ }
47176
+ const nextTotal = typeof prefetchedClipTotals?.[categoryId] === "number" ? Math.max(0, prefetchedClipTotals[categoryId]) : clips.length;
47177
+ if (next[categoryId] === nextTotal) {
47178
+ return;
47179
+ }
47180
+ next[categoryId] = nextTotal;
47181
+ changed = true;
47182
+ });
47183
+ return changed ? next : prev;
47184
+ });
47185
+ }, [prefetchedClipTotals, prefetchedPercentileClips]);
46487
47186
  const { state: filterState } = useClipFilter();
47187
+ const shouldShowCategory = useCallback((categoryId) => {
47188
+ switch (categoryId) {
47189
+ case "fast-cycles":
47190
+ return showPercentileCycleFilters && filterState.showFastCycles;
47191
+ case "slow-cycles":
47192
+ return showPercentileCycleFilters && filterState.showSlowCycles;
47193
+ case "longest-idles":
47194
+ return false;
47195
+ // filterState.showLongestIdles; // Temporarily disabled
47196
+ case "cycle_completion":
47197
+ return filterState.showCycleCompletion;
47198
+ case "idle_time":
47199
+ return filterState.showIdleTime;
47200
+ case "sop_deviations":
47201
+ return filterState.showSopDeviations;
47202
+ default:
47203
+ return true;
47204
+ }
47205
+ }, [filterState, showPercentileCycleFilters]);
46488
47206
  const [percentileCounts, setPercentileCounts] = useState({
46489
47207
  "fast-cycles": null,
46490
47208
  "slow-cycles": null
@@ -46594,11 +47312,12 @@ var FileManagerFilters = ({
46594
47312
  return null;
46595
47313
  }
46596
47314
  }, [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]);
47315
+ 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
47316
  const fetchClipMetadataPage = useCallback(async (categoryId, page = 1) => {
46599
47317
  if (!workspaceId || !date || shift === void 0) {
46600
47318
  throw new Error("Missing required params for clip metadata fetch");
46601
47319
  }
47320
+ const shouldScopeToTimeFilter = isCategoryScopedByTimeFilter(categoryId);
46602
47321
  const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
46603
47322
  method: "POST",
46604
47323
  headers: {
@@ -46612,9 +47331,12 @@ var FileManagerFilters = ({
46612
47331
  category: categoryId,
46613
47332
  page,
46614
47333
  limit: CLIP_METADATA_PAGE_SIZE,
46615
- knownTotal: typeof counts?.[categoryId] === "number" ? counts[categoryId] : null,
47334
+ knownTotal: shouldScopeToTimeFilter ? null : typeof counts?.[categoryId] === "number" ? counts[categoryId] : null,
46616
47335
  snapshotDateTime,
46617
47336
  snapshotClipId,
47337
+ startTime: shouldScopeToTimeFilter ? startTime : void 0,
47338
+ endTime: shouldScopeToTimeFilter ? endTime : void 0,
47339
+ timeFilterTimezone: shouldScopeToTimeFilter ? activeTimeFilterTimezone : void 0,
46618
47340
  sort: categoryId === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
46619
47341
  }),
46620
47342
  redirectReason: "session_expired"
@@ -46623,7 +47345,20 @@ var FileManagerFilters = ({
46623
47345
  throw new Error(`API error: ${response.status}`);
46624
47346
  }
46625
47347
  return response.json();
46626
- }, [workspaceId, date, shift, counts, snapshotDateTime, snapshotClipId, idleClipSort, supabase]);
47348
+ }, [
47349
+ activeTimeFilterTimezone,
47350
+ endTime,
47351
+ isCategoryScopedByTimeFilter,
47352
+ startTime,
47353
+ workspaceId,
47354
+ date,
47355
+ shift,
47356
+ counts,
47357
+ snapshotDateTime,
47358
+ snapshotClipId,
47359
+ idleClipSort,
47360
+ supabase
47361
+ ]);
46627
47362
  const seedIdleClassifications = useCallback(async (clips) => {
46628
47363
  if (!idleTimeVlmEnabled || clips.length === 0) {
46629
47364
  return;
@@ -46691,6 +47426,12 @@ var FileManagerFilters = ({
46691
47426
  ...prev,
46692
47427
  [categoryId]: page === 1 ? data.clips : [...prev[categoryId] || [], ...data.clips]
46693
47428
  }));
47429
+ if (isCategoryScopedByTimeFilter(categoryId)) {
47430
+ setScopedCategoryTotals((prev) => ({
47431
+ ...prev,
47432
+ [categoryId]: Math.max(0, Number(data.total || 0))
47433
+ }));
47434
+ }
46694
47435
  if (categoryId === "idle_time" && idleTimeVlmEnabled) {
46695
47436
  await seedIdleClassifications(data.clips || []);
46696
47437
  }
@@ -46707,7 +47448,94 @@ var FileManagerFilters = ({
46707
47448
  return newSet;
46708
47449
  });
46709
47450
  }
46710
- }, [workspaceId, date, shift, fetchClipMetadataPage, idleTimeVlmEnabled, seedIdleClassifications, getMetadataLoadingKey]);
47451
+ }, [workspaceId, date, shift, fetchClipMetadataPage, idleTimeVlmEnabled, seedIdleClassifications, getMetadataLoadingKey, isCategoryScopedByTimeFilter]);
47452
+ useCallback(async (categoryId) => {
47453
+ if (!workspaceId || !date || shift === void 0) {
47454
+ console.warn("[FileManager] Missing required params for full clip metadata fetch");
47455
+ return;
47456
+ }
47457
+ const loadingKey = `${getMetadataLoadingKey(categoryId, 1)}-all`;
47458
+ if (inFlightMetadataRequestsRef.current.has(loadingKey)) {
47459
+ return;
47460
+ }
47461
+ inFlightMetadataRequestsRef.current.add(loadingKey);
47462
+ setLoadingCategories((prev) => /* @__PURE__ */ new Set([...prev, loadingKey]));
47463
+ try {
47464
+ let accumulatedClips = [];
47465
+ let currentPage = 0;
47466
+ let hasMore = true;
47467
+ while (hasMore) {
47468
+ const nextPage = currentPage + 1;
47469
+ const pageData = await fetchClipMetadataPage(categoryId, nextPage);
47470
+ const pageClips = pageData.clips || [];
47471
+ accumulatedClips = [...accumulatedClips, ...pageClips];
47472
+ currentPage = nextPage;
47473
+ hasMore = Boolean(pageData.hasMore && pageClips.length > 0);
47474
+ }
47475
+ const dedupedClips = accumulatedClips.filter((clip, index, arr) => {
47476
+ const clipKey = clip.clipId || clip.id;
47477
+ return arr.findIndex((item) => (item.clipId || item.id) === clipKey) === index;
47478
+ });
47479
+ setClipMetadata((prev) => ({
47480
+ ...prev,
47481
+ [categoryId]: dedupedClips
47482
+ }));
47483
+ setCategoryPages((prev) => ({ ...prev, [categoryId]: Math.max(currentPage, 1) }));
47484
+ setCategoryHasMore((prev) => ({ ...prev, [categoryId]: false }));
47485
+ if (categoryId === "idle_time" && idleTimeVlmEnabled) {
47486
+ await seedIdleClassifications(dedupedClips);
47487
+ }
47488
+ console.log(`[FileManager] Loaded all ${dedupedClips.length} clips for ${categoryId} due to chart time filter`);
47489
+ } catch (error) {
47490
+ console.error("[FileManager] Error fetching full clip metadata:", error);
47491
+ } finally {
47492
+ inFlightMetadataRequestsRef.current.delete(loadingKey);
47493
+ setLoadingCategories((prev) => {
47494
+ const newSet = new Set(prev);
47495
+ newSet.delete(loadingKey);
47496
+ return newSet;
47497
+ });
47498
+ }
47499
+ }, [
47500
+ workspaceId,
47501
+ date,
47502
+ shift,
47503
+ fetchClipMetadataPage,
47504
+ idleTimeVlmEnabled,
47505
+ seedIdleClassifications,
47506
+ getMetadataLoadingKey
47507
+ ]);
47508
+ const isInitialTimeFilterCategory = useCallback((categoryId) => {
47509
+ if (!startTime || !endTime || !activeInitialTimeFilter) {
47510
+ return false;
47511
+ }
47512
+ if (activeFilter === categoryId) {
47513
+ return true;
47514
+ }
47515
+ if (activeInitialTimeFilter.categoryId === categoryId) {
47516
+ return true;
47517
+ }
47518
+ return Array.isArray(activeInitialTimeFilter.categoryIds) && activeInitialTimeFilter.categoryIds.includes(categoryId);
47519
+ }, [
47520
+ activeFilter,
47521
+ activeInitialTimeFilter,
47522
+ endTime,
47523
+ startTime
47524
+ ]);
47525
+ const hasCompleteMetadataForInitialTimeFilter = useCallback((categoryId) => {
47526
+ if (!isInitialTimeFilterCategory(categoryId)) {
47527
+ return true;
47528
+ }
47529
+ const loadedClips = clipMetadataRef.current[categoryId] || [];
47530
+ const knownTotal = typeof scopedCategoryTotals[categoryId] === "number" ? scopedCategoryTotals[categoryId] : null;
47531
+ if (categoryHasMore[categoryId]) {
47532
+ return false;
47533
+ }
47534
+ if (knownTotal !== null && loadedClips.length < knownTotal) {
47535
+ return false;
47536
+ }
47537
+ return loadedClips.length > 0 || knownTotal === 0;
47538
+ }, [categoryHasMore, isInitialTimeFilterCategory, scopedCategoryTotals]);
46711
47539
  const ensureAllIdleTimeClipMetadataLoaded = useCallback(async () => {
46712
47540
  if (!workspaceId || !date || shift === void 0) {
46713
47541
  return;
@@ -46773,6 +47601,12 @@ var FileManagerFilters = ({
46773
47601
  console.warn("[FileManager] Missing required params for percentile clips fetch");
46774
47602
  return;
46775
47603
  }
47604
+ const shouldScopeToTimeFilter = isCategoryScopedByTimeFilter(type);
47605
+ const requestKey = `${type}:${date}:${shift}:${filterState.percentile}:${shouldScopeToTimeFilter ? activeTimeFilterKey : "unscoped"}`;
47606
+ if (inFlightPercentileClipRequestsRef.current.has(requestKey)) {
47607
+ return;
47608
+ }
47609
+ inFlightPercentileClipRequestsRef.current.add(requestKey);
46776
47610
  try {
46777
47611
  const startDate = `${date}T00:00:00Z`;
46778
47612
  const endDate = `${date}T23:59:59Z`;
@@ -46788,7 +47622,10 @@ var FileManagerFilters = ({
46788
47622
  endDate,
46789
47623
  percentile: filterState.percentile,
46790
47624
  shiftId: shift,
46791
- limit: 50,
47625
+ limit: shouldScopeToTimeFilter ? 500 : 100,
47626
+ startTime: shouldScopeToTimeFilter ? startTime : void 0,
47627
+ endTime: shouldScopeToTimeFilter ? endTime : void 0,
47628
+ timeFilterTimezone: shouldScopeToTimeFilter ? activeTimeFilterTimezone : void 0,
46792
47629
  // The actual percentile action (fast-cycles, slow-cycles, idle-times)
46793
47630
  percentileAction: type
46794
47631
  }),
@@ -46803,7 +47640,7 @@ var FileManagerFilters = ({
46803
47640
  [type]: data.clips || []
46804
47641
  }));
46805
47642
  setPercentileCounts((prev) => {
46806
- if (typeof prev[type] === "number") {
47643
+ if (!shouldScopeToTimeFilter && typeof prev[type] === "number") {
46807
47644
  return prev;
46808
47645
  }
46809
47646
  return {
@@ -46814,8 +47651,206 @@ var FileManagerFilters = ({
46814
47651
  console.log(`[FileManager] Loaded ${data.clips?.length || 0} ${type} clips (percentile: ${filterState.percentile}%)`);
46815
47652
  } catch (error) {
46816
47653
  console.error(`[FileManager] Error fetching ${type} clips:`, error);
47654
+ } finally {
47655
+ inFlightPercentileClipRequestsRef.current.delete(requestKey);
47656
+ }
47657
+ }, [
47658
+ activeTimeFilterTimezone,
47659
+ activeTimeFilterKey,
47660
+ endTime,
47661
+ isCategoryScopedByTimeFilter,
47662
+ startTime,
47663
+ workspaceId,
47664
+ date,
47665
+ shift,
47666
+ filterState.percentile,
47667
+ showPercentileCycleFilters,
47668
+ supabase
47669
+ ]);
47670
+ useEffect(() => {
47671
+ if (!manualHourlySnapshotKey) {
47672
+ return;
47673
+ }
47674
+ if (manualHourlySnapshotAppliedKeyRef.current === manualHourlySnapshotKey) {
47675
+ return;
47676
+ }
47677
+ if (manualHourlySnapshotInFlightKeyRef.current === manualHourlySnapshotKey) {
47678
+ return;
47679
+ }
47680
+ manualHourlySnapshotInFlightKeyRef.current = manualHourlySnapshotKey;
47681
+ setManualHourlySnapshotFailureKey((prev) => prev === manualHourlySnapshotKey ? null : prev);
47682
+ const applyHourlySnapshot = async () => {
47683
+ try {
47684
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
47685
+ method: "POST",
47686
+ headers: {
47687
+ "Content-Type": "application/json"
47688
+ },
47689
+ body: JSON.stringify({
47690
+ action: "hourly-snapshot",
47691
+ workspaceId,
47692
+ date,
47693
+ shift,
47694
+ startTime,
47695
+ endTime,
47696
+ timeFilterTimezone: activeTimeFilterTimezone
47697
+ }),
47698
+ redirectReason: "session_expired"
47699
+ });
47700
+ if (!response.ok) {
47701
+ throw new Error(`API error: ${response.status}`);
47702
+ }
47703
+ const snapshot = await response.json();
47704
+ const metadataByCategory = snapshot?.metadataByCategory && typeof snapshot.metadataByCategory === "object" ? snapshot.metadataByCategory : {};
47705
+ const percentileClipsByCategory = snapshot?.percentileClipsByCategory && typeof snapshot.percentileClipsByCategory === "object" ? snapshot.percentileClipsByCategory : {};
47706
+ const rawTotalsByCategory = snapshot?.totalsByCategory && typeof snapshot.totalsByCategory === "object" ? snapshot.totalsByCategory : {};
47707
+ const totalsByCategory = requiredHourlyFilterCategoryIds.reduce((accumulator, categoryId) => {
47708
+ const rawTotal = rawTotalsByCategory[categoryId];
47709
+ if (typeof rawTotal === "number" && Number.isFinite(rawTotal)) {
47710
+ accumulator[categoryId] = Math.max(0, rawTotal);
47711
+ return accumulator;
47712
+ }
47713
+ if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
47714
+ accumulator[categoryId] = Array.isArray(percentileClipsByCategory[categoryId]) ? percentileClipsByCategory[categoryId].length : 0;
47715
+ return accumulator;
47716
+ }
47717
+ accumulator[categoryId] = Array.isArray(metadataByCategory[categoryId]) ? metadataByCategory[categoryId].length : 0;
47718
+ return accumulator;
47719
+ }, {});
47720
+ setClipMetadata((prev) => {
47721
+ const next = { ...prev };
47722
+ requiredHourlyFilterCategoryIds.forEach((categoryId) => {
47723
+ if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
47724
+ return;
47725
+ }
47726
+ next[categoryId] = Array.isArray(metadataByCategory[categoryId]) ? metadataByCategory[categoryId] : [];
47727
+ });
47728
+ return next;
47729
+ });
47730
+ setScopedCategoryTotals((prev) => ({
47731
+ ...prev,
47732
+ ...totalsByCategory
47733
+ }));
47734
+ if (showPercentileCycleFilters) {
47735
+ setPercentileClips((prev) => ({
47736
+ ...prev,
47737
+ "fast-cycles": Array.isArray(percentileClipsByCategory["fast-cycles"]) ? percentileClipsByCategory["fast-cycles"] : [],
47738
+ "slow-cycles": Array.isArray(percentileClipsByCategory["slow-cycles"]) ? percentileClipsByCategory["slow-cycles"] : []
47739
+ }));
47740
+ setPercentileCounts((prev) => ({
47741
+ ...prev,
47742
+ "fast-cycles": totalsByCategory["fast-cycles"] ?? 0,
47743
+ "slow-cycles": totalsByCategory["slow-cycles"] ?? 0
47744
+ }));
47745
+ }
47746
+ setCategoryPages((prev) => {
47747
+ const next = { ...prev };
47748
+ requiredHourlyFilterCategoryIds.forEach((categoryId) => {
47749
+ next[categoryId] = 1;
47750
+ });
47751
+ return next;
47752
+ });
47753
+ setCategoryHasMore((prev) => {
47754
+ const next = { ...prev };
47755
+ requiredHourlyFilterCategoryIds.forEach((categoryId) => {
47756
+ next[categoryId] = false;
47757
+ });
47758
+ return next;
47759
+ });
47760
+ const idleClips = metadataByCategory.idle_time;
47761
+ if (Array.isArray(idleClips) && idleClips.length > 0) {
47762
+ await seedIdleClassifications(idleClips);
47763
+ }
47764
+ manualHourlySnapshotAppliedKeyRef.current = manualHourlySnapshotKey;
47765
+ setManualHourlySnapshotFailureKey((prev) => prev === manualHourlySnapshotKey ? null : prev);
47766
+ } catch (error) {
47767
+ console.error("[FileManager] Error fetching manual hourly snapshot:", error);
47768
+ setManualHourlySnapshotFailureKey(manualHourlySnapshotKey);
47769
+ } finally {
47770
+ if (manualHourlySnapshotInFlightKeyRef.current === manualHourlySnapshotKey) {
47771
+ manualHourlySnapshotInFlightKeyRef.current = null;
47772
+ }
47773
+ }
47774
+ };
47775
+ void applyHourlySnapshot();
47776
+ }, [
47777
+ activeTimeFilterTimezone,
47778
+ date,
47779
+ endTime,
47780
+ manualHourlySnapshotKey,
47781
+ requiredHourlyFilterCategoryIds,
47782
+ seedIdleClassifications,
47783
+ shift,
47784
+ showPercentileCycleFilters,
47785
+ startTime,
47786
+ supabase,
47787
+ workspaceId
47788
+ ]);
47789
+ useEffect(() => {
47790
+ if (!startTime || !endTime || !activeInitialTimeFilter || initialTimeFilterCategoryIds.length === 0) {
47791
+ return;
46817
47792
  }
46818
- }, [workspaceId, date, shift, filterState.percentile, showPercentileCycleFilters, supabase]);
47793
+ initialTimeFilterCategoryIds.forEach((categoryId) => {
47794
+ if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
47795
+ const hasScopedPercentileResult = typeof percentileCounts[categoryId] === "number";
47796
+ if (!isCategoryExternallyManaged(categoryId) && !hasScopedPercentileResult && showPercentileCycleFilters) {
47797
+ fetchPercentileClips(categoryId);
47798
+ }
47799
+ return;
47800
+ }
47801
+ if (isCategoryExternallyManaged(categoryId)) {
47802
+ return;
47803
+ }
47804
+ if (!hasCompleteMetadataForInitialTimeFilter(categoryId)) {
47805
+ fetchClipMetadata(categoryId, 1);
47806
+ }
47807
+ });
47808
+ }, [
47809
+ activeInitialTimeFilter,
47810
+ endTime,
47811
+ fetchClipMetadata,
47812
+ fetchPercentileClips,
47813
+ hasCompleteMetadataForInitialTimeFilter,
47814
+ initialTimeFilterCategoryIds,
47815
+ isCategoryExternallyManaged,
47816
+ percentileCounts,
47817
+ showPercentileCycleFilters,
47818
+ startTime
47819
+ ]);
47820
+ useEffect(() => {
47821
+ if (!startTime || !endTime || !isTimeFilterActive || activeInitialTimeFilter) {
47822
+ return;
47823
+ }
47824
+ categoriesForTree.forEach((category) => {
47825
+ if (!shouldShowCategory(category.id) || isCategoryExternallyManaged(category.id)) {
47826
+ return;
47827
+ }
47828
+ if (typeof scopedCategoryTotals[category.id] !== "number") {
47829
+ fetchClipMetadata(category.id, 1);
47830
+ }
47831
+ });
47832
+ if (showPercentileCycleFilters) {
47833
+ if (!isCategoryExternallyManaged("fast-cycles") && typeof percentileCounts["fast-cycles"] !== "number") {
47834
+ fetchPercentileClips("fast-cycles");
47835
+ }
47836
+ if (!isCategoryExternallyManaged("slow-cycles") && typeof percentileCounts["slow-cycles"] !== "number") {
47837
+ fetchPercentileClips("slow-cycles");
47838
+ }
47839
+ }
47840
+ }, [
47841
+ activeInitialTimeFilter,
47842
+ categoriesForTree,
47843
+ endTime,
47844
+ fetchClipMetadata,
47845
+ fetchPercentileClips,
47846
+ isCategoryExternallyManaged,
47847
+ isTimeFilterActive,
47848
+ percentileCounts,
47849
+ scopedCategoryTotals,
47850
+ shouldShowCategory,
47851
+ showPercentileCycleFilters,
47852
+ startTime
47853
+ ]);
46819
47854
  const percentileCountsKey = useMemo(() => {
46820
47855
  if (!workspaceId || !date || shift === void 0) {
46821
47856
  return null;
@@ -46838,12 +47873,15 @@ var FileManagerFilters = ({
46838
47873
  }
46839
47874
  percentileCountsKeyRef.current = percentileCountsKey;
46840
47875
  percentilePrefetchRef.current = { key: null, types: /* @__PURE__ */ new Set() };
47876
+ if (prefetchedPercentileClips && (Array.isArray(prefetchedPercentileClips["fast-cycles"]) || Array.isArray(prefetchedPercentileClips["slow-cycles"]))) {
47877
+ return;
47878
+ }
46841
47879
  setPercentileCounts({
46842
47880
  "fast-cycles": null,
46843
47881
  "slow-cycles": null
46844
47882
  });
46845
47883
  setPercentileClips({});
46846
- }, [showPercentileCycleFilters, percentileCountsKey]);
47884
+ }, [prefetchedPercentileClips, showPercentileCycleFilters, percentileCountsKey]);
46847
47885
  useEffect(() => {
46848
47886
  if (!showPercentileCycleFilters) {
46849
47887
  return;
@@ -46899,11 +47937,20 @@ var FileManagerFilters = ({
46899
47937
  const data = await response.json();
46900
47938
  const fastCycles = data?.counts?.["fast-cycles"];
46901
47939
  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
- }));
47940
+ if (typeof fastCycles === "number" || typeof slowCycles === "number") {
47941
+ setPercentileCounts((prev) => {
47942
+ const nextFastCycles = typeof fastCycles === "number" ? fastCycles : prev["fast-cycles"];
47943
+ const nextSlowCycles = typeof slowCycles === "number" ? slowCycles : prev["slow-cycles"];
47944
+ if (prev["fast-cycles"] === nextFastCycles && prev["slow-cycles"] === nextSlowCycles) {
47945
+ return prev;
47946
+ }
47947
+ return {
47948
+ ...prev,
47949
+ "fast-cycles": nextFastCycles,
47950
+ "slow-cycles": nextSlowCycles
47951
+ };
47952
+ });
47953
+ }
46907
47954
  if (options?.prefetchClips) {
46908
47955
  if (percentilePrefetchRef.current.key !== requestKey) {
46909
47956
  percentilePrefetchRef.current = { key: requestKey, types: /* @__PURE__ */ new Set() };
@@ -46922,37 +47969,30 @@ var FileManagerFilters = ({
46922
47969
  }
46923
47970
  }, [workspaceId, date, shift, filterState.percentile, showPercentileCycleFilters, supabase, percentileCounts, percentileClips, fetchPercentileClips]);
46924
47971
  useEffect(() => {
46925
- if (!showPercentileCycleFilters || !isReady || !percentileCountsKey) {
47972
+ if (!showPercentileCycleFilters || !isReady || !percentileCountsKey || isTimeFilterActive) {
46926
47973
  return;
46927
47974
  }
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;
47975
+ fetchPercentileCounts({ prefetchClips: true });
47976
+ }, [showPercentileCycleFilters, isReady, percentileCountsKey, fetchPercentileCounts, isTimeFilterActive]);
47977
+ useEffect(() => {
47978
+ if (!isReady || isTimeFilterActive || activeFilter !== RECENT_FLOW_RED_STREAK_CLIP_TYPE2) {
47979
+ return;
46954
47980
  }
46955
- }, [filterState, showPercentileCycleFilters]);
47981
+ ["cycle_completion", "idle_time"].forEach((categoryId) => {
47982
+ if ((counts?.[categoryId] || 0) <= 0 || hasKnownClipMetadata(categoryId) || isCategoryExternallyManaged(categoryId)) {
47983
+ return;
47984
+ }
47985
+ fetchClipMetadata(categoryId, 1);
47986
+ });
47987
+ }, [
47988
+ activeFilter,
47989
+ counts,
47990
+ fetchClipMetadata,
47991
+ hasKnownClipMetadata,
47992
+ isCategoryExternallyManaged,
47993
+ isReady,
47994
+ isTimeFilterActive
47995
+ ]);
46956
47996
  const getPercentileIcon = useCallback((type, isExpanded, colorClasses) => {
46957
47997
  const iconMap = {
46958
47998
  "fast-cycles": { icon: TrendingUp, color: "text-green-600" },
@@ -46981,12 +48021,29 @@ var FileManagerFilters = ({
46981
48021
  newExpanded.add(activeFilter);
46982
48022
  return newExpanded;
46983
48023
  });
46984
- const category = categories.find((cat) => cat.id === activeFilter);
46985
- if (category && !isCategoryExternallyManaged(activeFilter) && !clipMetadataRef.current[activeFilter]) {
46986
- fetchClipMetadata(activeFilter, 1);
48024
+ const category = categoriesForTree.find((cat) => cat.id === activeFilter);
48025
+ if (category) {
48026
+ if (isInitialTimeFilterCategory(activeFilter) && !isCategoryExternallyManaged(activeFilter) && !hasCompleteMetadataForInitialTimeFilter(activeFilter)) {
48027
+ fetchClipMetadata(activeFilter, 1);
48028
+ } else if (!isCategoryExternallyManaged(activeFilter) && !clipMetadataRef.current[activeFilter]) {
48029
+ fetchClipMetadata(activeFilter, 1);
48030
+ }
46987
48031
  }
46988
48032
  }
46989
48033
  }, [activeFilter]);
48034
+ useEffect(() => {
48035
+ const requestedCategory = activeFilter;
48036
+ if (!requestedCategory || !isInitialTimeFilterCategory(requestedCategory) || isCategoryExternallyManaged(requestedCategory) || hasCompleteMetadataForInitialTimeFilter(requestedCategory)) {
48037
+ return;
48038
+ }
48039
+ fetchClipMetadata(requestedCategory, 1);
48040
+ }, [
48041
+ activeFilter,
48042
+ fetchClipMetadata,
48043
+ hasCompleteMetadataForInitialTimeFilter,
48044
+ isCategoryExternallyManaged,
48045
+ isInitialTimeFilterCategory
48046
+ ]);
46990
48047
  useEffect(() => {
46991
48048
  const handleEscape = (e) => {
46992
48049
  if (e.key === "Escape") {
@@ -47050,25 +48107,34 @@ var FileManagerFilters = ({
47050
48107
  }
47051
48108
  try {
47052
48109
  const clipDate = new Date(clipTimestamp);
47053
- const clipTimeStr = clipDate.toLocaleTimeString("en-US", {
48110
+ const clipParts = new Intl.DateTimeFormat("en-US", {
47054
48111
  hour12: false,
47055
48112
  hour: "2-digit",
47056
48113
  minute: "2-digit",
47057
- timeZone: timezone
47058
- });
47059
- return clipTimeStr >= startTime && clipTimeStr <= endTime;
48114
+ timeZone: activeTimeFilterTimezone
48115
+ }).formatToParts(clipDate);
48116
+ const hourValue = clipParts.find((part) => part.type === "hour")?.value;
48117
+ const minuteValue = clipParts.find((part) => part.type === "minute")?.value;
48118
+ const clipMinute = timeValueToMinutes(`${hourValue}:${minuteValue}`);
48119
+ return clipMinute === null ? false : isMinuteInTimeWindow(clipMinute, startTime, endTime);
47060
48120
  } catch (error) {
47061
48121
  console.error("[FileManager] Error parsing clip timestamp:", error);
47062
48122
  return true;
47063
48123
  }
47064
- }, [isTimeFilterActive, startTime, endTime, timezone]);
48124
+ }, [isTimeFilterActive, startTime, endTime, activeTimeFilterTimezone]);
47065
48125
  const filterTree = useMemo(() => {
47066
48126
  const tree = [];
47067
48127
  const regularCategoryNodes = [];
47068
- categories.forEach((category) => {
48128
+ categoriesForTree.forEach((category) => {
48129
+ if (category.id === "fast-cycles" || category.id === "slow-cycles") {
48130
+ return;
48131
+ }
47069
48132
  const categoryCount = counts?.[category.id] || 0;
47070
- const categoryClips = clipMetadata[category.id] || [];
47071
- let filteredClips = categoryClips.filter((clip) => isClipInTimeRange(clip.clip_timestamp));
48133
+ const categoryMetadataClips = getAvailableClipMetadata(category.id);
48134
+ const categoryVideoFallbackClips = categoryMetadataClips.length === 0 && isInitialTimeFilterCategory(category.id) ? videos.filter((video) => video.type === category.id).map(buildClipMetadataFromVideo).filter((clip) => isClipInTimeRange(clip.clip_timestamp)) : [];
48135
+ const categoryClips = categoryMetadataClips.length > 0 ? categoryMetadataClips : categoryVideoFallbackClips;
48136
+ const timeFilteredClips = categoryClips.filter((clip) => isClipInTimeRange(clip.clip_timestamp));
48137
+ let filteredClips = timeFilteredClips;
47072
48138
  if (category.id === RECENT_FLOW_RED_STREAK_CLIP_TYPE2) {
47073
48139
  filteredClips = sortRedFlowMetadata(filteredClips);
47074
48140
  }
@@ -47078,9 +48144,22 @@ var FileManagerFilters = ({
47078
48144
  return classification?.label === idleLabelFilter;
47079
48145
  });
47080
48146
  }
47081
- const displayCount = isTimeFilterActive || category.id === "idle_time" && idleLabelFilter ? filteredClips.length : categoryCount;
48147
+ const scopedTotal = typeof scopedCategoryTotals[category.id] === "number" ? scopedCategoryTotals[category.id] : null;
48148
+ const isScopedByTimeFilter = isCategoryScopedByTimeFilter(category.id);
48149
+ const scopedPageLoaded = isScopedByTimeFilter && typeof scopedCategoryTotals[category.id] === "number";
48150
+ const scopedResponseHadOutOfHourClips = categoryClips.length > filteredClips.length;
48151
+ const shouldTrustScopedTotal = !scopedResponseHadOutOfHourClips || Boolean(categoryHasMore[category.id]);
48152
+ const isCategoryMetadataLoading = Array.from(loadingCategories).some((key) => key.startsWith(`${category.id}-`));
48153
+ const isScopedTotalPending = isTimeFilterActive && isScopedByTimeFilter && scopedTotal === null;
48154
+ const displayCount = isScopedTotalPending ? null : isTimeFilterActive && isScopedByTimeFilter && scopedTotal !== null ? shouldTrustScopedTotal ? scopedTotal : filteredClips.length : isTimeFilterActive || category.id === "idle_time" && idleLabelFilter ? filteredClips.length : categoryCount;
48155
+ const shouldShowScopedEmptyCategory = Boolean(
48156
+ scopedPageLoaded && !isCategoryMetadataLoading && !categoryHasMore[category.id] && filteredClips.length === 0 && (isRequiredHourlyFilterCategory(category.id) || activeFilter === category.id)
48157
+ );
48158
+ const shouldShowScopedLoadingCategory = Boolean(
48159
+ isScopedTotalPending && (isRequiredHourlyFilterCategory(category.id) || !activeInitialTimeFilter || activeFilter === category.id)
48160
+ );
47082
48161
  const shouldShowEmptyIdleTime = category.id === "idle_time" && idleLabelFilter;
47083
- if ((displayCount > 0 || shouldShowEmptyIdleTime) && shouldShowCategory(category.id)) {
48162
+ if ((typeof displayCount === "number" && displayCount > 0 || shouldShowEmptyIdleTime || isCategoryMetadataLoading || shouldShowScopedEmptyCategory || shouldShowScopedLoadingCategory) && shouldShowCategory(category.id)) {
47084
48163
  const colorClasses = getColorClasses(category.color);
47085
48164
  const clipNodes = filteredClips.map((clip, index) => {
47086
48165
  const cycleTime = extractCycleTimeSeconds(clip);
@@ -47090,7 +48169,7 @@ var FileManagerFilters = ({
47090
48169
  const baseTimeLabel = formatClipExplorerTimeLabel({
47091
48170
  categoryId: category.id,
47092
48171
  clipTimestamp: clip.clip_timestamp,
47093
- timezone,
48172
+ timezone: activeTimeFilterTimezone,
47094
48173
  durationSeconds: idleDuration ?? clip.duration,
47095
48174
  idleStartTime: clip.idle_start_time,
47096
48175
  idleEndTime: clip.idle_end_time,
@@ -47128,6 +48207,7 @@ var FileManagerFilters = ({
47128
48207
  type: "category",
47129
48208
  count: displayCount,
47130
48209
  // Use filtered count when time filter is active
48210
+ countLoading: isScopedTotalPending || isCategoryMetadataLoading && scopedTotal === null,
47131
48211
  children: clipNodes,
47132
48212
  // Use clip nodes from metadata
47133
48213
  icon: expandedNodes.has(category.id) ? /* @__PURE__ */ jsx(FolderOpen, { className: `h-4 w-4 ${colorClasses.text}` }) : getCategoryIcon(category.icon, colorClasses),
@@ -47135,10 +48215,18 @@ var FileManagerFilters = ({
47135
48215
  });
47136
48216
  }
47137
48217
  });
47138
- const filteredFastCycles = (percentileClips["fast-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""));
47139
- const filteredSlowCycles = (percentileClips["slow-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""));
48218
+ const filteredFastCycles = sortPercentileCycleClipsForDisplay(
48219
+ "fast-cycles",
48220
+ (percentileClips["fast-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""))
48221
+ );
48222
+ const filteredSlowCycles = sortPercentileCycleClipsForDisplay(
48223
+ "slow-cycles",
48224
+ (percentileClips["slow-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""))
48225
+ );
47140
48226
  const fastCount = typeof percentileCounts["fast-cycles"] === "number" ? percentileCounts["fast-cycles"] : null;
47141
48227
  const slowCount = typeof percentileCounts["slow-cycles"] === "number" ? percentileCounts["slow-cycles"] : null;
48228
+ const isFastCountPending = showPercentileCycleFilters && fastCount === null;
48229
+ const isSlowCountPending = showPercentileCycleFilters && slowCount === null;
47142
48230
  const percentileCategories = showPercentileCycleFilters ? [
47143
48231
  {
47144
48232
  id: "fast-cycles",
@@ -47147,6 +48235,7 @@ var FileManagerFilters = ({
47147
48235
  description: "Top 10% fastest performance",
47148
48236
  type: "percentile-category",
47149
48237
  count: isTimeFilterActive ? fastCount === null && filteredFastCycles.length === 0 ? null : filteredFastCycles.length : fastCount,
48238
+ countLoading: isFastCountPending,
47150
48239
  icon: getPercentileIcon("fast-cycles", expandedNodes.has("fast-cycles"), { text: "text-green-600" }),
47151
48240
  color: "green",
47152
48241
  percentileType: "fast-cycles",
@@ -47158,7 +48247,7 @@ var FileManagerFilters = ({
47158
48247
  label: `${formatClipExplorerTimeLabel({
47159
48248
  categoryId: "fast-cycles",
47160
48249
  clipTimestamp: clip.creation_timestamp || clip.timestamp || "",
47161
- timezone,
48250
+ timezone: activeTimeFilterTimezone,
47162
48251
  durationSeconds: cycleTime
47163
48252
  })}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
47164
48253
  type: "video",
@@ -47178,6 +48267,7 @@ var FileManagerFilters = ({
47178
48267
  description: "Bottom 10% slowest performance",
47179
48268
  type: "percentile-category",
47180
48269
  count: isTimeFilterActive ? slowCount === null && filteredSlowCycles.length === 0 ? null : filteredSlowCycles.length : slowCount,
48270
+ countLoading: isSlowCountPending,
47181
48271
  icon: getPercentileIcon("slow-cycles", expandedNodes.has("slow-cycles"), { text: "text-red-600" }),
47182
48272
  color: "red",
47183
48273
  percentileType: "slow-cycles",
@@ -47189,7 +48279,7 @@ var FileManagerFilters = ({
47189
48279
  label: `${formatClipExplorerTimeLabel({
47190
48280
  categoryId: "slow-cycles",
47191
48281
  clipTimestamp: clip.creation_timestamp || clip.timestamp || "",
47192
- timezone,
48282
+ timezone: activeTimeFilterTimezone,
47193
48283
  durationSeconds: cycleTime
47194
48284
  })}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
47195
48285
  type: "video",
@@ -47220,7 +48310,7 @@ var FileManagerFilters = ({
47220
48310
  hour12: true,
47221
48311
  hour: 'numeric',
47222
48312
  minute: '2-digit',
47223
- timeZone: timezone
48313
+ timeZone: activeTimeFilterTimezone
47224
48314
  });
47225
48315
 
47226
48316
  return {
@@ -47239,7 +48329,11 @@ var FileManagerFilters = ({
47239
48329
  const orderedIds = [RECENT_FLOW_RED_STREAK_CLIP_TYPE2, "cycle_completion", "fast-cycles", "slow-cycles", "idle_time"];
47240
48330
  orderedIds.forEach((orderedId) => {
47241
48331
  const percentileCategory = percentileCategories.find((cat) => cat.id === orderedId);
47242
- const shouldIncludePercentile = percentileCategory ? typeof percentileCategory.count === "number" && percentileCategory.count > 0 : false;
48332
+ const shouldIncludePercentile = percentileCategory ? typeof percentileCategory.count === "number" || Boolean(
48333
+ isTimeFilterActive && isRequiredHourlyFilterCategory(orderedId) && typeof percentileCategory.count === "number" && percentileCategory.count === 0
48334
+ ) || Boolean(
48335
+ percentileCategory.countLoading && (isRequiredHourlyFilterCategory(orderedId) || !activeInitialTimeFilter || activeFilter === orderedId)
48336
+ ) : false;
47243
48337
  if (percentileCategory && shouldIncludePercentile && shouldShowCategory(orderedId)) {
47244
48338
  tree.push(percentileCategory);
47245
48339
  }
@@ -47254,23 +48348,263 @@ var FileManagerFilters = ({
47254
48348
  }
47255
48349
  });
47256
48350
  percentileCategories.forEach((category) => {
47257
- const shouldIncludePercentile = typeof category.count === "number" && category.count > 0;
48351
+ const shouldIncludePercentile = typeof category.count === "number" || Boolean(
48352
+ isTimeFilterActive && isRequiredHourlyFilterCategory(category.id) && typeof category.count === "number" && category.count === 0
48353
+ ) || Boolean(
48354
+ category.countLoading && (isRequiredHourlyFilterCategory(category.id) || !activeInitialTimeFilter || activeFilter === category.id)
48355
+ );
47258
48356
  if (!orderedIds.includes(category.id) && shouldIncludePercentile && shouldShowCategory(category.id)) {
47259
48357
  tree.push(category);
47260
48358
  }
47261
48359
  });
47262
48360
  return tree;
47263
- }, [categories, expandedNodes, counts, clipMetadata, percentileCounts, percentileClips, shouldShowCategory, getPercentileIcon, isClipInTimeRange, isTimeFilterActive, showPercentileCycleFilters]);
48361
+ }, [categoriesForTree, expandedNodes, counts, getAvailableClipMetadata, percentileCounts, percentileClips, shouldShowCategory, getPercentileIcon, isClipInTimeRange, isTimeFilterActive, showPercentileCycleFilters, loadingCategories, activeTimeFilterTimezone, isInitialTimeFilterCategory, isRequiredHourlyFilterCategory, hasCompleteMetadataForInitialTimeFilter, scopedCategoryTotals, isCategoryScopedByTimeFilter, categoryHasMore, activeFilter]);
48362
+ const chartHandoffVideoFallbackTree = useMemo(() => {
48363
+ if (!startTime || !endTime || !activeInitialTimeFilter || !activeFilter) {
48364
+ return [];
48365
+ }
48366
+ const fallbackCategory = categoriesForTree.find((category) => category.id === activeFilter) || categoriesForTree.find((category) => videos.some((video) => video.type === category.id));
48367
+ if (!fallbackCategory) {
48368
+ return [];
48369
+ }
48370
+ const fallbackVideos = videos.filter((video) => video.type === fallbackCategory.id);
48371
+ const sourceVideos = fallbackVideos.map(buildClipMetadataFromVideo).filter((clip) => isClipInTimeRange(clip.clip_timestamp));
48372
+ if (sourceVideos.length === 0) {
48373
+ return [];
48374
+ }
48375
+ const colorClasses = getColorClasses(fallbackCategory.color);
48376
+ const clipNodes = sourceVideos.map((clip, index) => {
48377
+ const cycleTime = extractCycleTimeSeconds(clip);
48378
+ const baseTimeLabel = formatClipExplorerTimeLabel({
48379
+ categoryId: fallbackCategory.id,
48380
+ clipTimestamp: clip.clip_timestamp,
48381
+ timezone: activeTimeFilterTimezone,
48382
+ durationSeconds: clip.duration,
48383
+ idleStartTime: clip.idle_start_time,
48384
+ idleEndTime: clip.idle_end_time,
48385
+ clipStartTime: clip.clip_start_time,
48386
+ clipEndTime: clip.clip_end_time
48387
+ });
48388
+ 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)` : ""}`;
48389
+ return {
48390
+ id: clip.id,
48391
+ label: displayLabel,
48392
+ type: "video",
48393
+ icon: getSeverityIcon(clip.severity, fallbackCategory.id, cycleTime, resolvedTargetCycleTime, clip.clipId),
48394
+ timestamp: clip.clip_timestamp,
48395
+ severity: clip.severity,
48396
+ clipId: clip.clipId,
48397
+ categoryId: fallbackCategory.id,
48398
+ clipPosition: index + 1,
48399
+ cycleTimeSeconds: cycleTime,
48400
+ duration: clip.duration,
48401
+ cycleItemCount: null,
48402
+ redFlowSeverityScore: clip.red_flow_severity_score ?? null,
48403
+ redFlowExplanationSummary: clip.red_flow_explanation_summary ?? clip.red_flow_explanation?.summary ?? null
48404
+ };
48405
+ });
48406
+ return [{
48407
+ id: activeFilter,
48408
+ label: fallbackCategory.id === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? RECENT_FLOW_RED_STREAK_DISPLAY_LABEL : fallbackCategory.label,
48409
+ subtitle: fallbackCategory.subtitle || fallbackCategory.description,
48410
+ description: fallbackCategory.description,
48411
+ type: "category",
48412
+ count: clipNodes.length,
48413
+ children: clipNodes,
48414
+ icon: /* @__PURE__ */ jsx(FolderOpen, { className: `h-4 w-4 ${colorClasses.text}` }),
48415
+ color: fallbackCategory.color
48416
+ }];
48417
+ }, [
48418
+ activeFilter,
48419
+ activeInitialTimeFilter,
48420
+ activeTimeFilterTimezone,
48421
+ categoriesForTree,
48422
+ counts,
48423
+ currentVideoId,
48424
+ endTime,
48425
+ getDisplayValue,
48426
+ isClipInTimeRange,
48427
+ resolvedTargetCycleTime,
48428
+ startTime,
48429
+ videos
48430
+ ]);
48431
+ const displayedFilterTree = useMemo(() => {
48432
+ if (chartHandoffVideoFallbackTree.length === 0) {
48433
+ return filterTree;
48434
+ }
48435
+ if (filterTree.length === 0) {
48436
+ return chartHandoffVideoFallbackTree;
48437
+ }
48438
+ const fallbackNode = chartHandoffVideoFallbackTree[0];
48439
+ const activeNode = filterTree.find((node) => node.id === activeFilter);
48440
+ const activeNodeHasChildren = (activeNode?.children?.length || 0) > 0;
48441
+ const shouldReplaceActiveNode = Boolean(
48442
+ activeFilter && fallbackNode && activeNode && !activeNodeHasChildren && isInitialTimeFilterCategory(activeFilter)
48443
+ );
48444
+ if (!shouldReplaceActiveNode) {
48445
+ return filterTree;
48446
+ }
48447
+ return filterTree.map((node) => node.id === activeFilter ? fallbackNode : node);
48448
+ }, [activeFilter, chartHandoffVideoFallbackTree, filterTree, isInitialTimeFilterCategory]);
48449
+ const getSelectionContextForNodes = useCallback((categoryId, clipNodes) => {
48450
+ const normalizeSeverity = (severity) => severity === "low" || severity === "medium" || severity === "high" ? severity : "medium";
48451
+ const scopedNodes = clipNodes.filter((node) => node.type === "video" && Boolean(node.clipId || node.id));
48452
+ const scopedNodeClips = scopedNodes.map((node, index) => ({
48453
+ id: node.clipId || node.id,
48454
+ clipId: node.clipId || node.id,
48455
+ clip_timestamp: node.timestamp || "",
48456
+ creation_timestamp: node.timestamp || "",
48457
+ description: node.label,
48458
+ severity: normalizeSeverity(node.severity),
48459
+ category: categoryId,
48460
+ duration: node.duration,
48461
+ cycle_time_seconds: node.cycleTimeSeconds,
48462
+ index
48463
+ }));
48464
+ const categoryClips = getAvailableClipMetadata(categoryId);
48465
+ const contextClips = isCategoryScopedByTimeFilter(categoryId) && scopedNodeClips.length > 0 ? scopedNodeClips : categoryClips.length > 0 ? categoryClips : scopedNodeClips;
48466
+ const scopedTotal = scopedCategoryTotals[categoryId];
48467
+ const total = typeof scopedTotal === "number" ? scopedTotal : counts?.[categoryId];
48468
+ return contextClips.length ? { clips: contextClips, total } : void 0;
48469
+ }, [counts, getAvailableClipMetadata, isCategoryScopedByTimeFilter, scopedCategoryTotals]);
48470
+ useEffect(() => {
48471
+ if (!activeInitialTimeFilter || !startTime || !endTime || currentVideoId || !onClipSelect) {
48472
+ return;
48473
+ }
48474
+ const categoryPriority = Array.from(new Set([
48475
+ activeInitialTimeFilter.categoryId,
48476
+ activeFilter,
48477
+ ...activeInitialTimeFilter.categoryIds || []
48478
+ ].filter((value) => typeof value === "string" && value.length > 0)));
48479
+ const preferredCategoryId = categoryPriority[0];
48480
+ const orderedCategoryNodes = displayedFilterTree.filter((categoryNode) => isInitialTimeFilterCategory(categoryNode.id)).sort((left, right) => {
48481
+ const leftIndex = categoryPriority.indexOf(left.id);
48482
+ const rightIndex = categoryPriority.indexOf(right.id);
48483
+ const leftPriority = leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex;
48484
+ const rightPriority = rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex;
48485
+ return leftPriority - rightPriority;
48486
+ });
48487
+ const preferredCategoryNode = preferredCategoryId ? orderedCategoryNodes.find((categoryNode) => categoryNode.id === preferredCategoryId) : void 0;
48488
+ if (preferredCategoryId && !preferredCategoryNode) {
48489
+ const preferredKnownTotal = preferredCategoryId === "fast-cycles" || preferredCategoryId === "slow-cycles" ? percentileCounts[preferredCategoryId] : scopedCategoryTotals[preferredCategoryId];
48490
+ if (preferredKnownTotal !== 0) {
48491
+ return;
48492
+ }
48493
+ }
48494
+ const preferredFirstClipNode = preferredCategoryNode?.children?.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
48495
+ if (preferredCategoryId && preferredCategoryNode && !preferredFirstClipNode) {
48496
+ const preferredKnownTotal = preferredCategoryId === "fast-cycles" || preferredCategoryId === "slow-cycles" ? percentileCounts[preferredCategoryId] : scopedCategoryTotals[preferredCategoryId];
48497
+ if (preferredKnownTotal !== 0) {
48498
+ return;
48499
+ }
48500
+ }
48501
+ for (const categoryNode of orderedCategoryNodes) {
48502
+ const firstClipNode = categoryNode.children?.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
48503
+ if (!firstClipNode?.clipId || !firstClipNode.categoryId) {
48504
+ continue;
48505
+ }
48506
+ const selectionKey = `${activeTimeFilterKey}:${firstClipNode.categoryId}:${firstClipNode.clipId}:${currentVideoId || "none"}`;
48507
+ if (autoSelectedScopedClipRef.current === selectionKey) {
48508
+ return;
48509
+ }
48510
+ autoSelectedScopedClipRef.current = selectionKey;
48511
+ const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, categoryNode.children || []);
48512
+ onClipSelect(
48513
+ firstClipNode.categoryId,
48514
+ firstClipNode.clipId,
48515
+ firstClipNode.clipPosition,
48516
+ selectionContext
48517
+ );
48518
+ return;
48519
+ }
48520
+ }, [
48521
+ activeInitialTimeFilter,
48522
+ activeFilter,
48523
+ activeTimeFilterKey,
48524
+ currentVideoId,
48525
+ displayedFilterTree,
48526
+ endTime,
48527
+ getSelectionContextForNodes,
48528
+ isInitialTimeFilterCategory,
48529
+ onClipSelect,
48530
+ percentileCounts,
48531
+ scopedCategoryTotals,
48532
+ startTime
48533
+ ]);
48534
+ const isChartHandoffLoading = useMemo(() => {
48535
+ if (!startTime || !endTime || !activeInitialTimeFilter || !activeFilter || !isInitialTimeFilterCategory(activeFilter)) {
48536
+ return false;
48537
+ }
48538
+ const pageOneLoadingKey = getMetadataLoadingKey(activeFilter, 1);
48539
+ return Boolean(
48540
+ activeCategoryLoading || Array.from(loadingCategories).some((key) => key === pageOneLoadingKey || key.startsWith(`${pageOneLoadingKey}-all`))
48541
+ );
48542
+ }, [
48543
+ activeCategoryLoading,
48544
+ activeFilter,
48545
+ activeInitialTimeFilter,
48546
+ endTime,
48547
+ getMetadataLoadingKey,
48548
+ isInitialTimeFilterCategory,
48549
+ loadingCategories,
48550
+ startTime
48551
+ ]);
48552
+ useEffect(() => {
48553
+ if (!isTimeFilterActive || !startTime || !endTime || !activeFilter || activeFilter === "all" || !onClipSelect) {
48554
+ return;
48555
+ }
48556
+ const activeCategoryNode = displayedFilterTree.find((categoryNode) => categoryNode.id === activeFilter);
48557
+ const activeClipNodes = (activeCategoryNode?.children || []).filter((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
48558
+ if (activeClipNodes.length === 0) {
48559
+ return;
48560
+ }
48561
+ const currentClipIsVisible = Boolean(
48562
+ currentVideoId && activeClipNodes.some((child) => (child.clipId || child.id) === currentVideoId)
48563
+ );
48564
+ if (currentClipIsVisible) {
48565
+ return;
48566
+ }
48567
+ const firstClipNode = activeClipNodes[0];
48568
+ if (!firstClipNode?.clipId || !firstClipNode.categoryId) {
48569
+ return;
48570
+ }
48571
+ const selectionKey = `${activeTimeFilterKey}:${firstClipNode.categoryId}:${firstClipNode.clipId}:${currentVideoId || "none"}`;
48572
+ if (autoSelectedScopedClipRef.current === selectionKey) {
48573
+ return;
48574
+ }
48575
+ autoSelectedScopedClipRef.current = selectionKey;
48576
+ const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, activeClipNodes);
48577
+ onClipSelect(
48578
+ firstClipNode.categoryId,
48579
+ firstClipNode.clipId,
48580
+ firstClipNode.clipPosition,
48581
+ selectionContext
48582
+ );
48583
+ }, [
48584
+ activeFilter,
48585
+ activeTimeFilterKey,
48586
+ currentVideoId,
48587
+ displayedFilterTree,
48588
+ endTime,
48589
+ getSelectionContextForNodes,
48590
+ isTimeFilterActive,
48591
+ onClipSelect,
48592
+ startTime
48593
+ ]);
47264
48594
  const toggleExpanded = (nodeId) => {
47265
48595
  const newExpanded = new Set(expandedNodes);
47266
48596
  if (newExpanded.has(nodeId)) {
47267
48597
  newExpanded.delete(nodeId);
47268
48598
  } else {
47269
48599
  newExpanded.add(nodeId);
47270
- const category = categories.find((cat) => cat.id === nodeId);
47271
- if (category && !clipMetadata[nodeId] && !isCategoryExternallyManaged(nodeId)) {
48600
+ const category = categoriesForTree.find((cat) => cat.id === nodeId);
48601
+ if (category) {
47272
48602
  console.log(`[FileManager] Fetching clips for expanded category: ${nodeId}`);
47273
- fetchClipMetadata(nodeId, 1);
48603
+ if (isInitialTimeFilterCategory(nodeId) && !hasCompleteMetadataForInitialTimeFilter(nodeId)) {
48604
+ fetchClipMetadata(nodeId, 1);
48605
+ } else if (!hasKnownClipMetadata(nodeId) && !isCategoryExternallyManaged(nodeId)) {
48606
+ fetchClipMetadata(nodeId, 1);
48607
+ }
47274
48608
  }
47275
48609
  if (!isCategoryExternallyManaged(nodeId) && showPercentileCycleFilters && nodeId === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
47276
48610
  fetchPercentileClips("fast-cycles");
@@ -47288,10 +48622,14 @@ var FileManagerFilters = ({
47288
48622
  newExpanded.delete(node.id);
47289
48623
  } else {
47290
48624
  newExpanded.add(node.id);
47291
- const category = categories.find((cat) => cat.id === node.id);
47292
- if (category && !clipMetadata[node.id] && !isCategoryExternallyManaged(node.id)) {
48625
+ const category = categoriesForTree.find((cat) => cat.id === node.id);
48626
+ if (category) {
47293
48627
  console.log(`[FileManager] Fetching clips for expanded category: ${node.id}`);
47294
- fetchClipMetadata(node.id, 1);
48628
+ if (isInitialTimeFilterCategory(node.id) && !hasCompleteMetadataForInitialTimeFilter(node.id)) {
48629
+ fetchClipMetadata(node.id, 1);
48630
+ } else if (!hasKnownClipMetadata(node.id) && !isCategoryExternallyManaged(node.id)) {
48631
+ fetchClipMetadata(node.id, 1);
48632
+ }
47295
48633
  }
47296
48634
  if (!isCategoryExternallyManaged(node.id) && showPercentileCycleFilters && node.id === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
47297
48635
  fetchPercentileClips("fast-cycles");
@@ -47301,7 +48639,21 @@ var FileManagerFilters = ({
47301
48639
  }
47302
48640
  }
47303
48641
  setExpandedNodes(newExpanded);
47304
- onFilterChange(node.id);
48642
+ if (node.id !== activeFilter) {
48643
+ onFilterChange(node.id);
48644
+ }
48645
+ if (isCategoryScopedByTimeFilter(node.id) && onClipSelect && node.children?.length) {
48646
+ const firstClipNode = node.children.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
48647
+ if (firstClipNode?.clipId && firstClipNode.categoryId) {
48648
+ const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, node.children);
48649
+ onClipSelect(
48650
+ firstClipNode.categoryId,
48651
+ firstClipNode.clipId,
48652
+ firstClipNode.clipPosition,
48653
+ selectionContext
48654
+ );
48655
+ }
48656
+ }
47305
48657
  if (node.id === "fast-cycles") {
47306
48658
  trackCoreEvent("Fast Clips Clicked", {
47307
48659
  workspaceId,
@@ -47339,8 +48691,11 @@ var FileManagerFilters = ({
47339
48691
  } else if (node.type === "video") {
47340
48692
  if (onClipSelect && node.categoryId !== void 0 && node.clipId !== void 0) {
47341
48693
  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;
48694
+ const categoryNode = displayedFilterTree.find((candidate) => candidate.id === node.categoryId);
48695
+ const selectionContext = getSelectionContextForNodes(
48696
+ node.categoryId,
48697
+ categoryNode?.children || [node]
48698
+ );
47344
48699
  onClipSelect(node.categoryId, node.clipId, node.clipPosition, selectionContext);
47345
48700
  } else {
47346
48701
  const videoIndex = videos.findIndex((v) => v.id === node.id);
@@ -47353,24 +48708,40 @@ var FileManagerFilters = ({
47353
48708
  const renderNode = (node, depth = 0) => {
47354
48709
  const isExpanded = expandedNodes.has(node.id);
47355
48710
  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;
48711
+ const nodeClipId = node.clipId || node.id;
48712
+ const isCurrentVideo = Boolean(
48713
+ node.type === "video" && currentVideoId && currentVideoId === nodeClipId
48714
+ );
48715
+ const isCountUnknown = Boolean(
48716
+ (node.type === "category" || node.type === "percentile-category") && (node.count === null || node.countLoading)
48717
+ );
48718
+ const hasRenderedChildren = (node.children?.length || 0) > 0;
47360
48719
  const loadedPage = categoryPages[node.id] || 0;
47361
48720
  const pageOneLoadingKey = getMetadataLoadingKey(node.id, 1);
47362
48721
  const nextMetadataPage = loadedPage + 1;
47363
48722
  const nextLoadMorePage = (categoryPages[node.id] || 1) + 1;
47364
48723
  const isPageOneLoading = loadingCategories.has(pageOneLoadingKey);
48724
+ const isFullCategoryLoading = Array.from(loadingCategories).some((key) => key.startsWith(`${pageOneLoadingKey}-all`));
47365
48725
  const isLoadMoreLoading = loadedPage > 0 && loadingCategories.has(getMetadataLoadingKey(node.id, nextMetadataPage));
48726
+ const isNodeLoading = Boolean(
48727
+ isPageOneLoading || isFullCategoryLoading || activeCategoryLoading && node.id === activeFilter
48728
+ );
48729
+ const totalForLoadMore = isCategoryScopedByTimeFilter(node.id) && typeof scopedCategoryTotals[node.id] === "number" ? scopedCategoryTotals[node.id] : counts?.[node.id] || 0;
47366
48730
  const showInitialLoadingState = Boolean(
47367
- isExpanded && !hasLoadedChildren && (node.type === "category" || node.type === "percentile-category") && (isPageOneLoading || activeCategoryLoading && node.id === activeFilter)
48731
+ isExpanded && !hasRenderedChildren && (node.type === "category" || node.type === "percentile-category") && isNodeLoading
48732
+ );
48733
+ const showScopedEmptyState = Boolean(
48734
+ isExpanded && !hasRenderedChildren && !isNodeLoading && !categoryHasMore[node.id] && isCategoryScopedByTimeFilter(node.id) && (node.type === "category" || node.type === "percentile-category")
47368
48735
  );
48736
+ const hasChildren = isCountUnknown || (node.count || 0) > 0 || isNodeLoading || showScopedEmptyState;
47369
48737
  const colorClasses = node.color ? getColorClasses(node.color) : null;
47370
48738
  return /* @__PURE__ */ jsxs("div", { className: "select-none animate-in", children: [
47371
48739
  /* @__PURE__ */ jsxs(
47372
48740
  "div",
47373
48741
  {
48742
+ "aria-current": isCurrentVideo ? "true" : void 0,
48743
+ "data-qa-clips-row-clip-id": node.type === "video" ? nodeClipId : void 0,
48744
+ "data-qa-clips-row-category-id": node.type === "video" ? node.categoryId : void 0,
47374
48745
  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
48746
  onClick: () => handleNodeClick(node),
47376
48747
  children: [
@@ -47406,7 +48777,7 @@ var FileManagerFilters = ({
47406
48777
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between", children: [
47407
48778
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
47408
48779
  /* @__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 }),
48780
+ 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
48781
  node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
47411
48782
  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
48783
  // Show root cause label for idle time clips (text only, icon is on the left)
@@ -47442,7 +48813,7 @@ var FileManagerFilters = ({
47442
48813
  })()
47443
48814
  ) })
47444
48815
  ] }),
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 }) })
48816
+ 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
48817
  ] })
47447
48818
  ]
47448
48819
  }
@@ -47453,6 +48824,7 @@ var FileManagerFilters = ({
47453
48824
  /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
47454
48825
  "Loading clips..."
47455
48826
  ] }) }),
48827
+ showScopedEmptyState && /* @__PURE__ */ jsx("div", { className: "py-2 px-3 text-center text-sm text-slate-500", children: "No clips found" }),
47456
48828
  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
48829
  /* @__PURE__ */ jsx("div", { className: "animate-spin mr-2 h-4 w-4 border-2 border-slate-300 border-t-blue-500 rounded-full" }),
47458
48830
  "Loading more clips..."
@@ -47467,7 +48839,7 @@ var FileManagerFilters = ({
47467
48839
  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
48840
  children: [
47469
48841
  "Load more clips (",
47470
- (counts?.[node.id] || 0) - (clipMetadata[node.id]?.length || 0),
48842
+ Math.max(0, totalForLoadMore - getAvailableClipMetadata(node.id).length),
47471
48843
  " remaining)"
47472
48844
  ]
47473
48845
  }
@@ -47530,6 +48902,8 @@ var FileManagerFilters = ({
47530
48902
  e.stopPropagation();
47531
48903
  setStartTime("");
47532
48904
  setEndTime("");
48905
+ setActiveInitialTimeFilter(null);
48906
+ setScopedCategoryTotals({});
47533
48907
  setIsTimeFilterActive(false);
47534
48908
  },
47535
48909
  className: "rounded-full p-0.5 transition-colors hover:bg-blue-100",
@@ -47663,6 +49037,8 @@ var FileManagerFilters = ({
47663
49037
  onClick: () => {
47664
49038
  setStartTime("");
47665
49039
  setEndTime("");
49040
+ setActiveInitialTimeFilter(null);
49041
+ setScopedCategoryTotals({});
47666
49042
  setStartSearchTerm("");
47667
49043
  setEndSearchTerm("");
47668
49044
  setIsTimeFilterActive(false);
@@ -47751,8 +49127,13 @@ var FileManagerFilters = ({
47751
49127
  )
47752
49128
  ] }),
47753
49129
  /* @__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: [
49130
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: displayedFilterTree.map((node) => renderNode(node)) }),
49131
+ displayedFilterTree.length === 0 && isChartHandoffLoading && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
49132
+ /* @__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" }) }),
49133
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "Loading clips..." }),
49134
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Finding clips from the selected hour" })
49135
+ ] }),
49136
+ displayedFilterTree.length === 0 && !isChartHandoffLoading && (startTime || endTime) && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
47756
49137
  /* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(Clock, { className: "h-12 w-12 mx-auto" }) }),
47757
49138
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips found" }),
47758
49139
  /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500 mb-4", children: "No clips match the selected time range" }),
@@ -47762,6 +49143,8 @@ var FileManagerFilters = ({
47762
49143
  onClick: () => {
47763
49144
  setStartTime("");
47764
49145
  setEndTime("");
49146
+ setActiveInitialTimeFilter(null);
49147
+ setScopedCategoryTotals({});
47765
49148
  setIsTimeFilterActive(false);
47766
49149
  },
47767
49150
  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 +49152,12 @@ var FileManagerFilters = ({
47769
49152
  }
47770
49153
  )
47771
49154
  ] }),
47772
- filterTree.length === 0 && !startTime && !endTime && categories.length === 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
49155
+ displayedFilterTree.length === 0 && !startTime && !endTime && categories.length === 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
47773
49156
  /* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(HelpCircle, { className: "h-12 w-12 mx-auto" }) }),
47774
49157
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clip types available" }),
47775
49158
  /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "Loading clip categories..." })
47776
49159
  ] }),
47777
- filterTree.length === 0 && !startTime && !endTime && categories.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
49160
+ displayedFilterTree.length === 0 && !startTime && !endTime && categories.length > 0 && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
47778
49161
  /* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(Play, { className: "h-12 w-12 mx-auto" }) }),
47779
49162
  /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips available" }),
47780
49163
  /* @__PURE__ */ jsx("p", { className: "text-sm text-slate-500", children: "No clips found for the selected time period" })
@@ -48181,6 +49564,65 @@ function useClipsRealtimeUpdates({
48181
49564
  }
48182
49565
  var LOW_EFFICIENCY_CATEGORY_ID = "recent_flow_red_streak";
48183
49566
  var LOW_EFFICIENCY_AI_SUMMARY_ENABLED = process.env.NEXT_PUBLIC_LOW_EFFICIENCY_AI_SUMMARY_ENABLED === "true";
49567
+ var timeValueToMinutes2 = (value) => {
49568
+ const [hourValue, minuteValue] = value.substring(0, 5).split(":").map(Number);
49569
+ if (!Number.isInteger(hourValue) || !Number.isInteger(minuteValue) || hourValue < 0 || hourValue > 23 || minuteValue < 0 || minuteValue > 59) {
49570
+ return null;
49571
+ }
49572
+ return hourValue * 60 + minuteValue;
49573
+ };
49574
+ var isMinuteInTimeWindow2 = (minute, startValue, endValue) => {
49575
+ const startMinute = timeValueToMinutes2(startValue);
49576
+ const endMinute = timeValueToMinutes2(endValue);
49577
+ if (startMinute === null || endMinute === null) {
49578
+ return true;
49579
+ }
49580
+ return endMinute > startMinute ? minute >= startMinute && minute < endMinute : minute >= startMinute || minute < endMinute;
49581
+ };
49582
+ var CHART_HANDOFF_CATEGORY_FALLBACKS = {
49583
+ cycle_completion: {
49584
+ label: "Cycle Completion",
49585
+ description: "Successfully completed production cycles",
49586
+ color: "blue",
49587
+ icon: "check-circle"
49588
+ },
49589
+ "fast-cycles": {
49590
+ label: "Fast Cycles",
49591
+ description: "Fastest cycle clips",
49592
+ color: "green",
49593
+ icon: "trending-up"
49594
+ },
49595
+ "slow-cycles": {
49596
+ label: "Slow Cycles",
49597
+ description: "Slowest cycle clips",
49598
+ color: "red",
49599
+ icon: "trending-down"
49600
+ },
49601
+ idle_time: {
49602
+ label: "Idle Time",
49603
+ description: "Low value activities and idle moments",
49604
+ color: "amber",
49605
+ icon: "clock"
49606
+ },
49607
+ recent_flow_red_streak: {
49608
+ label: "Low moments",
49609
+ description: "Moments of low efficiency",
49610
+ color: "rose",
49611
+ icon: "alert-triangle"
49612
+ },
49613
+ worst_cycle_time: {
49614
+ label: "Slow Cycles",
49615
+ description: "Slowest cycle clips",
49616
+ color: "red",
49617
+ icon: "trending-down"
49618
+ },
49619
+ best_cycle_time: {
49620
+ label: "Fast Cycles",
49621
+ description: "Fastest cycle clips",
49622
+ color: "green",
49623
+ icon: "trending-up"
49624
+ }
49625
+ };
48184
49626
  var parseFiniteNumber2 = (value) => {
48185
49627
  if (typeof value === "number" && Number.isFinite(value)) {
48186
49628
  return value;
@@ -48228,6 +49670,7 @@ var BottlenecksContent = ({
48228
49670
  ticketId,
48229
49671
  prefetchedPercentileCounts,
48230
49672
  lowMomentsPrefetch,
49673
+ initialTimePrefetch,
48231
49674
  initialTimeFilter
48232
49675
  }) => {
48233
49676
  console.log("\u{1F3AB} [BottlenecksContent] Rendered with ticketId:", ticketId || "NONE", "workspaceId:", workspaceId, "date:", date, "shift:", shift);
@@ -48440,6 +49883,108 @@ var BottlenecksContent = ({
48440
49883
  enabled: isEffectiveShiftReady
48441
49884
  });
48442
49885
  const isLowMomentsCategoryAvailable = useMemo(() => Array.isArray(clipTypes) && clipTypes.some((type) => type?.type === LOW_EFFICIENCY_CATEGORY_ID || type?.id === LOW_EFFICIENCY_CATEGORY_ID), [clipTypes]);
49886
+ const requestedInitialCategoryCandidates = useMemo(() => {
49887
+ const requestedCategories = [
49888
+ ...Array.isArray(initialTimeFilter?.categoryIds) ? initialTimeFilter.categoryIds : [],
49889
+ initialTimeFilter?.categoryId
49890
+ ];
49891
+ return Array.from(
49892
+ new Set(
49893
+ requestedCategories.filter((category) => Boolean(category)).map((category) => category.trim()).filter(Boolean)
49894
+ )
49895
+ );
49896
+ }, [initialTimeFilter?.categoryId, initialTimeFilter?.categoryIds]);
49897
+ const hasInitialTimeHandoff = Boolean(initialTimeFilter?.startTime && initialTimeFilter?.endTime);
49898
+ const initialTimeHandoffCategorySet = useMemo(() => new Set(requestedInitialCategoryCandidates), [requestedInitialCategoryCandidates]);
49899
+ const isInitialTimeHandoffCategory = useCallback((categoryId) => Boolean(
49900
+ hasInitialTimeHandoff && categoryId && (initialTimeHandoffCategorySet.size === 0 || initialTimeHandoffCategorySet.has(categoryId))
49901
+ ), [hasInitialTimeHandoff, initialTimeHandoffCategorySet]);
49902
+ const isTimestampInInitialTimeHandoff = useCallback((timestamp) => {
49903
+ if (!hasInitialTimeHandoff || !initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !timestamp) {
49904
+ return true;
49905
+ }
49906
+ try {
49907
+ const clipDate = new Date(timestamp);
49908
+ if (Number.isNaN(clipDate.getTime())) {
49909
+ return false;
49910
+ }
49911
+ const clipParts = new Intl.DateTimeFormat("en-US", {
49912
+ hour12: false,
49913
+ hour: "2-digit",
49914
+ minute: "2-digit",
49915
+ timeZone: initialTimeFilter.timezone || timezone
49916
+ }).formatToParts(clipDate);
49917
+ const hourValue = clipParts.find((part) => part.type === "hour")?.value;
49918
+ const minuteValue = clipParts.find((part) => part.type === "minute")?.value;
49919
+ const clipMinute = timeValueToMinutes2(`${hourValue}:${minuteValue}`);
49920
+ return clipMinute === null ? false : isMinuteInTimeWindow2(clipMinute, initialTimeFilter.startTime, initialTimeFilter.endTime);
49921
+ } catch {
49922
+ return false;
49923
+ }
49924
+ }, [
49925
+ hasInitialTimeHandoff,
49926
+ initialTimeFilter?.endTime,
49927
+ initialTimeFilter?.startTime,
49928
+ initialTimeFilter?.timezone,
49929
+ timezone
49930
+ ]);
49931
+ const resolvedInitialCategory = useMemo(() => resolveInitialClipCategory(requestedInitialCategoryCandidates, clipTypes, dynamicCounts), [clipTypes, dynamicCounts, requestedInitialCategoryCandidates]);
49932
+ const initialTimeFilterKey = useMemo(() => hasInitialTimeHandoff ? [
49933
+ initialTimeFilter?.startTime,
49934
+ initialTimeFilter?.endTime,
49935
+ initialTimeFilter?.timezone || timezone,
49936
+ initialTimeFilter?.categoryId || "",
49937
+ ...initialTimeFilter?.categoryIds || []
49938
+ ].join("|") : "", [
49939
+ hasInitialTimeHandoff,
49940
+ initialTimeFilter?.categoryId,
49941
+ initialTimeFilter?.categoryIds,
49942
+ initialTimeFilter?.endTime,
49943
+ initialTimeFilter?.startTime,
49944
+ initialTimeFilter?.timezone,
49945
+ timezone
49946
+ ]);
49947
+ const preferredInitialTimeCategory = resolvedInitialCategory || requestedInitialCategoryCandidates[0] || "";
49948
+ useEffect(() => {
49949
+ if (!initialTimeFilterKey) {
49950
+ return;
49951
+ }
49952
+ setPendingVideo(null);
49953
+ setAllVideos([]);
49954
+ setCurrentIndex(0);
49955
+ setCurrentClipId(null);
49956
+ currentClipIdRef.current = null;
49957
+ setCurrentMetadataIndex(0);
49958
+ currentMetadataIndexRef.current = 0;
49959
+ setCurrentPosition(0);
49960
+ currentPositionRef.current = 0;
49961
+ setCurrentTotal(0);
49962
+ currentTotalRef.current = 0;
49963
+ setCategoryMetadata([]);
49964
+ categoryMetadataRef.current = [];
49965
+ setCategoryMetadataCategoryId(null);
49966
+ setCategoryMetadataSort(null);
49967
+ clearRetryTimeout();
49968
+ navigationLockRef.current = false;
49969
+ loadingCategoryRef.current = null;
49970
+ if (loadingTimeoutRef.current) {
49971
+ clearTimeout(loadingTimeoutRef.current);
49972
+ loadingTimeoutRef.current = null;
49973
+ }
49974
+ setIsTransitioning(false);
49975
+ setIsNavigating(false);
49976
+ setIsCategoryLoading(false);
49977
+ if (preferredInitialTimeCategory) {
49978
+ setInitialFilter(preferredInitialTimeCategory);
49979
+ updateActiveFilter(preferredInitialTimeCategory);
49980
+ previousFilterRef.current = "";
49981
+ }
49982
+ }, [
49983
+ clearRetryTimeout,
49984
+ initialTimeFilterKey,
49985
+ preferredInitialTimeCategory,
49986
+ updateActiveFilter
49987
+ ]);
48443
49988
  console.log("[BottlenecksContent] Clip types data:", {
48444
49989
  clipTypes,
48445
49990
  clipTypesLength: clipTypes?.length,
@@ -48487,6 +50032,15 @@ var BottlenecksContent = ({
48487
50032
  }
48488
50033
  });
48489
50034
  useEffect(() => {
50035
+ if (resolvedInitialCategory) {
50036
+ if (initialFilter !== resolvedInitialCategory) {
50037
+ setInitialFilter(resolvedInitialCategory);
50038
+ }
50039
+ if (activeFilterRef.current !== resolvedInitialCategory) {
50040
+ updateActiveFilter(resolvedInitialCategory);
50041
+ }
50042
+ return;
50043
+ }
48490
50044
  if (initialFilter) {
48491
50045
  return;
48492
50046
  }
@@ -48503,7 +50057,15 @@ var BottlenecksContent = ({
48503
50057
  activeFilterRef.current = defaultCategory;
48504
50058
  return;
48505
50059
  }
48506
- }, [clipTypes, dynamicCounts, defaultCategory, initialFilter, isLowMomentsCategoryAvailable]);
50060
+ }, [
50061
+ clipTypes,
50062
+ dynamicCounts,
50063
+ defaultCategory,
50064
+ initialFilter,
50065
+ isLowMomentsCategoryAvailable,
50066
+ resolvedInitialCategory,
50067
+ updateActiveFilter
50068
+ ]);
48507
50069
  const mergedCounts = useMemo(() => {
48508
50070
  return { ...dynamicCounts };
48509
50071
  }, [dynamicCounts]);
@@ -48552,6 +50114,9 @@ var BottlenecksContent = ({
48552
50114
  const loadFirstVideoForCategory = useCallback(async (category) => {
48553
50115
  if (!workspaceId || !s3ClipsService || !isMountedRef.current || !isEffectiveShiftReady) return;
48554
50116
  const targetCategory = category || activeFilterRef.current;
50117
+ if (targetCategory === "fast-cycles" || targetCategory === "slow-cycles") {
50118
+ return;
50119
+ }
48555
50120
  const operationKey = `loadFirstVideo:${targetCategory}:${effectiveDateString}:${effectiveShiftId}`;
48556
50121
  if (loadingCategoryRef.current === targetCategory || fetchInProgressRef.current.has(operationKey)) {
48557
50122
  console.log(`[BottlenecksContent] Load first video already in progress for ${targetCategory}`);
@@ -48997,8 +50562,9 @@ var BottlenecksContent = ({
48997
50562
  });
48998
50563
  }
48999
50564
  setVisibleCategoryMetadata(categoryId, cachedMetadata);
49000
- if (autoLoadFirstVideo && cachedMetadata.length > 0 && s3ClipsService) {
49001
- const firstClipMeta = cachedMetadata[0];
50565
+ const cachedAutoloadCandidates = isInitialTimeHandoffCategory(categoryId) ? cachedMetadata.filter((clip) => isTimestampInInitialTimeHandoff(clip.clip_timestamp || clip.creation_timestamp || clip.timestamp)) : cachedMetadata;
50566
+ if (autoLoadFirstVideo && cachedAutoloadCandidates.length > 0 && s3ClipsService) {
50567
+ const firstClipMeta = cachedAutoloadCandidates[0];
49002
50568
  const firstClipId = firstClipMeta.clipId || firstClipMeta.id;
49003
50569
  const prefetchedFirstVideo = matchingLowMomentsPrefetch?.firstVideo ?? null;
49004
50570
  try {
@@ -49037,7 +50603,10 @@ var BottlenecksContent = ({
49037
50603
  endDate: `${resolvedDate}T23:59:59Z`,
49038
50604
  percentile: 10,
49039
50605
  shiftId: effectiveShiftId,
49040
- limit: 100
50606
+ limit: isInitialTimeHandoffCategory(categoryId) ? 500 : 100,
50607
+ startTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.startTime : void 0,
50608
+ endTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.endTime : void 0,
50609
+ timeFilterTimezone: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.timezone || timezone : void 0
49041
50610
  }),
49042
50611
  redirectReason: "session_expired"
49043
50612
  });
@@ -49054,10 +50623,13 @@ var BottlenecksContent = ({
49054
50623
  shift: effectiveShiftId,
49055
50624
  category: categoryId,
49056
50625
  page: 1,
49057
- limit: 100,
49058
- knownTotal: mergedCounts[categoryId] ?? null,
50626
+ limit: isInitialTimeHandoffCategory(categoryId) ? 500 : 100,
50627
+ knownTotal: isInitialTimeHandoffCategory(categoryId) ? null : mergedCounts[categoryId] ?? null,
49059
50628
  snapshotDateTime,
49060
50629
  snapshotClipId,
50630
+ startTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.startTime : void 0,
50631
+ endTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.endTime : void 0,
50632
+ timeFilterTimezone: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.timezone || timezone : void 0,
49061
50633
  sort: categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
49062
50634
  }),
49063
50635
  redirectReason: "session_expired"
@@ -49070,7 +50642,8 @@ var BottlenecksContent = ({
49070
50642
  if (categoryData.clips && isMountedRef.current) {
49071
50643
  let metadataClips;
49072
50644
  if (isPercentileCategory(categoryId)) {
49073
- metadataClips = categoryData.clips.map((clip, index) => ({
50645
+ const sortedPercentileClips = sortPercentileCycleClipsForDisplay(categoryId, categoryData.clips);
50646
+ metadataClips = sortedPercentileClips.map((clip, index) => ({
49074
50647
  id: clip.id,
49075
50648
  clipId: clip.id,
49076
50649
  clip_timestamp: clip.creation_timestamp || clip.timestamp,
@@ -49117,8 +50690,9 @@ var BottlenecksContent = ({
49117
50690
  }));
49118
50691
  setVisibleCategoryMetadata(categoryId, metadataClips);
49119
50692
  console.log(`[BottlenecksContent] Loaded metadata for ${categoryId}: ${metadataClips.length} clips`);
49120
- if (autoLoadFirstVideo && metadataClips.length > 0 && s3ClipsService) {
49121
- const firstClipMeta = metadataClips[0];
50693
+ const autoloadCandidates = isInitialTimeHandoffCategory(categoryId) ? metadataClips.filter((clip) => isTimestampInInitialTimeHandoff(clip.clip_timestamp || clip.creation_timestamp || clip.timestamp)) : metadataClips;
50694
+ if (autoLoadFirstVideo && autoloadCandidates.length > 0 && s3ClipsService) {
50695
+ const firstClipMeta = autoloadCandidates[0];
49122
50696
  const firstClipId = firstClipMeta.clipId || firstClipMeta.id;
49123
50697
  try {
49124
50698
  const video = await s3ClipsService.getClipById(firstClipId);
@@ -49152,7 +50726,7 @@ var BottlenecksContent = ({
49152
50726
  setIsCategoryLoading(false);
49153
50727
  }
49154
50728
  }
49155
- }, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, idleClipSort, supabase, setVisibleCategoryMetadata, lowMomentsPrefetch, applyPrefetchedFirstVideo, applyMetadataSnapshot, isLowMomentsCategoryAvailable]);
50729
+ }, [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
50730
  useEffect(() => {
49157
50731
  if (activeFilter !== LOW_EFFICIENCY_CATEGORY_ID || !isLowMomentsCategoryAvailable) {
49158
50732
  return;
@@ -49184,6 +50758,37 @@ var BottlenecksContent = ({
49184
50758
  applyMetadataSnapshot,
49185
50759
  isLowMomentsCategoryAvailable
49186
50760
  ]);
50761
+ useEffect(() => {
50762
+ if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !initialTimePrefetch || initialTimePrefetch.loading || initialTimePrefetch.categoryId !== activeFilter) {
50763
+ return;
50764
+ }
50765
+ if (initialTimePrefetch.metadata.length > 0) {
50766
+ applyMetadataSnapshot(
50767
+ initialTimePrefetch.categoryId,
50768
+ initialTimePrefetch.metadata,
50769
+ initialTimePrefetch.total
50770
+ );
50771
+ }
50772
+ if (initialTimePrefetch.firstVideo && isTimestampInInitialTimeHandoff(
50773
+ initialTimePrefetch.firstVideo.creation_timestamp || initialTimePrefetch.firstVideo.timestamp
50774
+ )) {
50775
+ applyPrefetchedFirstVideo(initialTimePrefetch.firstVideo);
50776
+ if (isMountedRef.current) {
50777
+ setIsCategoryLoading(false);
50778
+ setIsInitialLoading(false);
50779
+ setIsTransitioning(false);
50780
+ setIsNavigating(false);
50781
+ }
50782
+ }
50783
+ }, [
50784
+ activeFilter,
50785
+ applyMetadataSnapshot,
50786
+ applyPrefetchedFirstVideo,
50787
+ initialTimeFilter?.endTime,
50788
+ initialTimeFilter?.startTime,
50789
+ initialTimePrefetch,
50790
+ isTimestampInInitialTimeHandoff
50791
+ ]);
49187
50792
  useEffect(() => {
49188
50793
  if (previousIdleClipSortRef.current === idleClipSort) {
49189
50794
  return;
@@ -49223,7 +50828,7 @@ var BottlenecksContent = ({
49223
50828
  currentTotalRef.current = total;
49224
50829
  setCurrentTotal(total);
49225
50830
  previousFilterRef.current = activeFilter;
49226
- const metadataLoadPlan = getCategoryMetadataLoadPlanForFilterChange({
50831
+ const metadataLoadPlan = isInitialTimeHandoffCategory(activeFilter) ? { shouldLoad: false, autoLoadFirstVideo: false } : getCategoryMetadataLoadPlanForFilterChange({
49227
50832
  activeFilter,
49228
50833
  currentClipId,
49229
50834
  categoryTotal: total
@@ -49247,7 +50852,7 @@ var BottlenecksContent = ({
49247
50852
  }
49248
50853
  }
49249
50854
  }
49250
- }, [activeFilter, allVideos, mergedCounts, currentClipId, loadCategoryMetadata]);
50855
+ }, [activeFilter, allVideos, mergedCounts, currentClipId, loadCategoryMetadata, isInitialTimeHandoffCategory]);
49251
50856
  useEffect(() => {
49252
50857
  if (!currentClipId || activeFilter === "all") {
49253
50858
  return;
@@ -49275,10 +50880,19 @@ var BottlenecksContent = ({
49275
50880
  console.warn("[BottlenecksContent] Error disposing player:", e);
49276
50881
  }
49277
50882
  }
49278
- loadingTimeoutRef.current = setTimeout(() => {
50883
+ if (loadingTimeoutRef.current) {
50884
+ clearTimeout(loadingTimeoutRef.current);
50885
+ loadingTimeoutRef.current = null;
50886
+ }
50887
+ const loadingTimeout = setTimeout(() => {
50888
+ if (loadingTimeoutRef.current !== loadingTimeout) {
50889
+ return;
50890
+ }
50891
+ loadingTimeoutRef.current = null;
49279
50892
  console.warn("[BottlenecksContent] Loading timeout - clearing stuck loading state");
49280
50893
  clearLoadingState();
49281
50894
  }, 2e3);
50895
+ loadingTimeoutRef.current = loadingTimeout;
49282
50896
  if (activeFilterRef.current !== categoryId) {
49283
50897
  updateActiveFilter(categoryId);
49284
50898
  }
@@ -49298,17 +50912,20 @@ var BottlenecksContent = ({
49298
50912
  setCurrentClipId(clipId);
49299
50913
  setAllVideos([video]);
49300
50914
  setCurrentIndex(0);
50915
+ clearLoadingState();
49301
50916
  } else {
49302
50917
  throw new Error(`Failed to load video data for clip ${clipId}`);
49303
50918
  }
49304
50919
  await metadataPromise;
49305
50920
  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) {
50921
+ const getMetadataClipId = (clip) => clip?.clipId || clip?.id;
50922
+ const metadataHasClip = (clips) => clips.some((clip) => getMetadataClipId(clip) === clipId);
50923
+ const fallbackHasClip = fallbackMetadata?.some((clip) => getMetadataClipId(clip) === clipId);
50924
+ if ((metadataArray.length === 0 || !metadataHasClip(metadataArray)) && fallbackHasClip) {
49308
50925
  applyMetadataSnapshot(categoryId, fallbackMetadata, fallbackTotal);
49309
50926
  metadataArray = fallbackMetadata;
49310
50927
  }
49311
- if (metadataArray.length === 0 || !metadataArray.some((clip) => clip.clipId === clipId)) {
50928
+ if (metadataArray.length === 0 || !metadataHasClip(metadataArray)) {
49312
50929
  if (!fallbackHasClip) {
49313
50930
  console.warn(`[BottlenecksContent] Clip ${clipId} not found in metadata for ${categoryId} (cache hit: ${metadataArray.length > 0}) - forcing refresh`);
49314
50931
  await loadCategoryMetadata(categoryId, false, true);
@@ -49327,7 +50944,7 @@ var BottlenecksContent = ({
49327
50944
  }
49328
50945
  return;
49329
50946
  }
49330
- const clickedClipIndex = metadataArray.findIndex((clip) => clip.clipId === clipId);
50947
+ const clickedClipIndex = metadataArray.findIndex((clip) => getMetadataClipId(clip) === clipId);
49331
50948
  if (clickedClipIndex === -1) {
49332
50949
  console.warn(`[BottlenecksContent] Clip ${clipId} not found after metadata refresh`);
49333
50950
  if (!shouldUseMetadataNavigation(categoryId)) {
@@ -49715,6 +51332,32 @@ var BottlenecksContent = ({
49715
51332
  }
49716
51333
  return filteredVideos[currentIndex];
49717
51334
  }, [filteredVideos, currentIndex]);
51335
+ const currentVideoMatchesInitialTimeHandoff = useMemo(() => {
51336
+ if (!hasInitialTimeHandoff) {
51337
+ return true;
51338
+ }
51339
+ if (!currentVideo) {
51340
+ return false;
51341
+ }
51342
+ if (!isInitialTimeHandoffCategory(activeFilter) || !isInitialTimeHandoffCategory(currentVideo.type)) {
51343
+ return false;
51344
+ }
51345
+ return isTimestampInInitialTimeHandoff(
51346
+ currentVideo.creation_timestamp || currentVideo.timestamp || currentVideo.clip_end_time || currentVideo.clip_start_time
51347
+ );
51348
+ }, [
51349
+ activeFilter,
51350
+ currentVideo,
51351
+ hasInitialTimeHandoff,
51352
+ isInitialTimeHandoffCategory,
51353
+ isTimestampInInitialTimeHandoff
51354
+ ]);
51355
+ const shouldHoldInitialTimeHandoffVideo = Boolean(
51356
+ hasInitialTimeHandoff && !currentVideoMatchesInitialTimeHandoff && (isCategoryLoading || filteredVideos.length > 0 || (mergedCounts[activeFilter] || 0) > 0)
51357
+ );
51358
+ const canRenderCurrentVideo = Boolean(
51359
+ filteredVideos.length > 0 && currentVideo && !isFullscreen && !shouldHoldInitialTimeHandoffVideo
51360
+ );
49718
51361
  const currentLowEfficiencyClipId = currentVideo?.id || currentClipId || null;
49719
51362
  const isCurrentLowEfficiencyClip = Boolean(
49720
51363
  currentVideo?.type === "recent_flow_red_streak" || currentVideo?.red_flow_timeline
@@ -49881,11 +51524,111 @@ var BottlenecksContent = ({
49881
51524
  }
49882
51525
  return currentPosition;
49883
51526
  }, [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(
51527
+ const initialTimePrefetchedMetadata = useMemo(() => {
51528
+ if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !initialTimePrefetch || initialTimePrefetch.loading) {
51529
+ return void 0;
51530
+ }
51531
+ if (initialTimePrefetch.metadataByCategory) {
51532
+ return initialTimePrefetch.metadataByCategory;
51533
+ }
51534
+ if (initialTimePrefetch.categoryId === activeFilter && initialTimePrefetch.metadata.length > 0) {
51535
+ return { [activeFilter]: initialTimePrefetch.metadata };
51536
+ }
51537
+ return void 0;
51538
+ }, [
49885
51539
  activeFilter,
51540
+ initialTimePrefetch,
51541
+ initialTimeFilter?.endTime,
51542
+ initialTimeFilter?.startTime
51543
+ ]);
51544
+ const prefetchedExplorerMetadata = useMemo(() => {
51545
+ if (initialTimePrefetchedMetadata) {
51546
+ return initialTimePrefetchedMetadata;
51547
+ }
51548
+ if (activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && !lowMomentsPrefetch.loading && lowMomentsPrefetch.metadata.length > 0) {
51549
+ return { [LOW_EFFICIENCY_CATEGORY_ID]: lowMomentsPrefetch.metadata };
51550
+ }
51551
+ if (activeFilter === "idle_time" && categoryMetadataSort !== idleClipSort) {
51552
+ return void 0;
51553
+ }
51554
+ const metadataSnapshot = buildPrefetchedExplorerMetadata(
51555
+ activeFilter,
51556
+ categoryMetadataCategoryId,
51557
+ categoryMetadata
51558
+ );
51559
+ if (metadataSnapshot) {
51560
+ return metadataSnapshot;
51561
+ }
51562
+ if (initialTimeFilter?.startTime && initialTimeFilter?.endTime && currentVideo && currentVideo.type === activeFilter) {
51563
+ return {
51564
+ [activeFilter]: [{
51565
+ id: currentVideo.id,
51566
+ clipId: currentVideo.id,
51567
+ clip_timestamp: currentVideo.creation_timestamp || currentVideo.timestamp,
51568
+ description: currentVideo.description,
51569
+ severity: currentVideo.severity,
51570
+ category: activeFilter,
51571
+ duration: typeof currentVideo.duration === "number" ? currentVideo.duration : typeof currentVideo.cycle_time_seconds === "number" ? currentVideo.cycle_time_seconds : void 0,
51572
+ clip_start_time: currentVideo.clip_start_time,
51573
+ clip_end_time: currentVideo.clip_end_time,
51574
+ index: 0,
51575
+ idle_start_time: currentVideo.idle_start_time,
51576
+ idle_end_time: currentVideo.idle_end_time,
51577
+ cycle_item_count: null,
51578
+ red_flow_timeline: currentVideo.red_flow_timeline,
51579
+ red_flow_severity_score: currentVideo.red_flow_severity_score,
51580
+ red_flow_output_shortfall_units: currentVideo.red_flow_output_shortfall_units,
51581
+ red_flow_worst_minute: currentVideo.red_flow_worst_minute,
51582
+ red_flow_explanation_summary: currentVideo.red_flow_explanation_summary,
51583
+ red_flow_explanation: currentVideo.red_flow_explanation
51584
+ }]
51585
+ };
51586
+ }
51587
+ return void 0;
51588
+ }, [
51589
+ activeFilter,
51590
+ categoryMetadata,
49886
51591
  categoryMetadataCategoryId,
49887
- categoryMetadata
49888
- ), [activeFilter, categoryMetadata, categoryMetadataCategoryId, categoryMetadataSort, idleClipSort, lowMomentsPrefetch, workspaceId, effectiveDateString, effectiveShiftId, isLowMomentsCategoryAvailable]);
51592
+ categoryMetadataSort,
51593
+ currentVideo,
51594
+ idleClipSort,
51595
+ initialTimePrefetchedMetadata,
51596
+ initialTimePrefetch,
51597
+ initialTimeFilter?.endTime,
51598
+ initialTimeFilter?.startTime,
51599
+ lowMomentsPrefetch,
51600
+ workspaceId,
51601
+ effectiveDateString,
51602
+ effectiveShiftId,
51603
+ isLowMomentsCategoryAvailable
51604
+ ]);
51605
+ const externallyManagedLoadingCategories = useMemo(() => {
51606
+ const managedCategories = {
51607
+ recent_flow_red_streak: Boolean(
51608
+ activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && lowMomentsPrefetch.loading
51609
+ )
51610
+ };
51611
+ const isInitialTimeHandoffPending = Boolean(
51612
+ initialTimeFilter?.startTime && initialTimeFilter?.endTime && requestedInitialCategoryCandidates.length > 0 && (!initialTimePrefetch || initialTimePrefetch.loading)
51613
+ );
51614
+ if (isInitialTimeHandoffPending) {
51615
+ requestedInitialCategoryCandidates.forEach((categoryId) => {
51616
+ managedCategories[categoryId] = true;
51617
+ });
51618
+ }
51619
+ return managedCategories;
51620
+ }, [
51621
+ activeFilter,
51622
+ effectiveDateString,
51623
+ effectiveShiftId,
51624
+ initialTimeFilter?.endTime,
51625
+ initialTimeFilter?.startTime,
51626
+ initialTimePrefetch,
51627
+ isLowMomentsCategoryAvailable,
51628
+ lowMomentsPrefetch,
51629
+ requestedInitialCategoryCandidates,
51630
+ workspaceId
51631
+ ]);
49889
51632
  const classificationClipIds = useMemo(() => {
49890
51633
  if (!idleTimeVlmEnabled) {
49891
51634
  return [];
@@ -50295,7 +52038,7 @@ var BottlenecksContent = ({
50295
52038
  /* @__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
52039
  ] });
50297
52040
  }
50298
- if (clipTypesLoading && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
52041
+ if (!hasInitialTimeHandoff && clipTypesLoading && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
50299
52042
  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
52043
  }
50301
52044
  if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
@@ -50305,7 +52048,30 @@ var BottlenecksContent = ({
50305
52048
  /* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
50306
52049
  ] });
50307
52050
  }
50308
- const categoriesToShow = clipTypes.length > 0 ? clipTypes : [];
52051
+ const categoriesToShow = (() => {
52052
+ const categories = clipTypes.length > 0 ? [...clipTypes] : [];
52053
+ const existingTypes = new Set(categories.map((category) => category.type));
52054
+ requestedInitialCategoryCandidates.forEach((categoryType) => {
52055
+ if (existingTypes.has(categoryType) || !hasInitialTimeHandoff && (mergedCounts[categoryType] || 0) <= 0) {
52056
+ return;
52057
+ }
52058
+ const fallback = CHART_HANDOFF_CATEGORY_FALLBACKS[categoryType];
52059
+ if (!fallback) {
52060
+ return;
52061
+ }
52062
+ categories.push({
52063
+ id: categoryType,
52064
+ type: categoryType,
52065
+ label: fallback.label,
52066
+ description: fallback.description,
52067
+ color: fallback.color,
52068
+ icon: fallback.icon,
52069
+ sort_order: 50
52070
+ });
52071
+ existingTypes.add(categoryType);
52072
+ });
52073
+ return categories;
52074
+ })();
50309
52075
  console.log("[BottlenecksContent] Categories to show:", {
50310
52076
  categoriesToShow,
50311
52077
  categoriesToShowLength: categoriesToShow?.length,
@@ -50324,190 +52090,204 @@ var BottlenecksContent = ({
50324
52090
  }
50325
52091
  ),
50326
52092
  /* @__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,
52093
+ /* @__PURE__ */ jsx(
52094
+ "div",
52095
+ {
52096
+ className: "min-w-0 w-full lg:flex-[3] lg:h-full",
52097
+ "data-qa-clips-active-filter": activeFilter || "",
52098
+ "data-qa-clips-current-video-type": currentVideo?.type || "",
52099
+ "data-qa-clips-current-video-id": currentVideo?.id || "",
52100
+ "data-qa-clips-current-clip-id": currentClipId || "",
52101
+ "data-qa-clips-filtered-count": filteredVideos.length,
52102
+ "data-qa-clips-all-count": allVideos.length,
52103
+ "data-qa-clips-current-video-matches-hour": String(currentVideoMatchesInitialTimeHandoff),
52104
+ "data-qa-clips-hold-hour-video": String(shouldHoldInitialTimeHandoffVideo),
52105
+ 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: [
52106
+ /* @__PURE__ */ jsx(
52107
+ "div",
50338
52108
  {
50339
- ref: videoRef,
50340
- src: currentVideo.src,
50341
- poster: "",
50342
52109
  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();
52110
+ style: {
52111
+ opacity: isTransitioning ? 0 : 1,
52112
+ transition: "opacity 0.1s ease-in-out"
50399
52113
  },
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"
52114
+ children: !shouldDeferPlayerRenderForCrop && /* @__PURE__ */ jsx(
52115
+ CroppedVideoPlayer,
52116
+ {
52117
+ ref: videoRef,
52118
+ src: currentVideo.src,
52119
+ poster: "",
52120
+ className: "w-full h-full",
52121
+ crop: workspaceCrop?.crop,
52122
+ autoplay: true,
52123
+ playsInline: true,
52124
+ loop: false,
52125
+ externalLoadingControl: true,
52126
+ onReady: handleVideoReady,
52127
+ onPlay: handleVideoPlay,
52128
+ onPause: handleVideoPause,
52129
+ onTimeUpdate: handleTimeUpdate,
52130
+ onDurationChange: handleDurationChange,
52131
+ onEnded: handleVideoEnded,
52132
+ onError: handleVideoError,
52133
+ onLoadedData: handleLoadedData,
52134
+ onPlaying: handleVideoPlaying,
52135
+ onLoadingChange: handleVideoLoadingChange,
52136
+ onShare: handleShareClip,
52137
+ isShareLoading,
52138
+ isShareCopied,
52139
+ timelineAnnotations: currentVideo.red_flow_timeline,
52140
+ timelineExplanation: currentVideo.red_flow_explanation,
52141
+ timelineTimezone: timezone,
52142
+ options: videoPlayerOptions
52143
+ },
52144
+ `${currentVideo.id}-${playerInstanceNonce}-inline`
52145
+ )
50402
52146
  }
50403
52147
  ),
50404
- error.canRetry && /* @__PURE__ */ jsx(
50405
- "button",
52148
+ currentVideo.type === "recent_flow_red_streak" && !shouldDeferPlayerRenderForCrop ? /* @__PURE__ */ jsx(
52149
+ RedFlowDiagnosticOverlay,
50406
52150
  {
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"
52151
+ timeline: currentVideo.red_flow_timeline,
52152
+ explanation: currentVideo.red_flow_explanation,
52153
+ aiSummary: currentLowEfficiencyAiSummary,
52154
+ aiSummaryLoading: isCurrentLowEfficiencyAiSummaryLoading,
52155
+ aiSummaryError: currentLowEfficiencyAiSummaryError,
52156
+ className: "right-4 top-4"
50413
52157
  }
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
- ] }) }) });
52158
+ ) : null,
52159
+ 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..." }) }),
52160
+ !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..." }) }),
52161
+ 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: [
52162
+ /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md" }),
52163
+ /* @__PURE__ */ jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
52164
+ ] }) }),
52165
+ 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: [
52166
+ /* @__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" }) }),
52167
+ /* @__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" }),
52168
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
52169
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-center", children: [
52170
+ error.canSkip && /* @__PURE__ */ jsx(
52171
+ "button",
52172
+ {
52173
+ onClick: () => {
52174
+ setError(null);
52175
+ videoRetryCountRef.current = 0;
52176
+ handleNext();
52177
+ },
52178
+ className: "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 rounded-md text-sm font-medium transition-colors",
52179
+ children: "Skip to Next Clip"
52180
+ }
52181
+ ),
52182
+ error.canRetry && /* @__PURE__ */ jsx(
52183
+ "button",
52184
+ {
52185
+ onClick: () => {
52186
+ videoRetryCountRef.current = 0;
52187
+ restartCurrentClipPlayback();
52188
+ },
52189
+ className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
52190
+ children: "Retry"
52191
+ }
52192
+ )
52193
+ ] })
52194
+ ] }) }),
52195
+ (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" ? (
52196
+ // Show full colored badge for idle time
52197
+ (() => {
52198
+ const classification = getIdleTimeClassification(currentVideo);
52199
+ const confidence = getIdleTimeConfidence(currentVideo);
52200
+ const config = getRootCauseConfig(classification);
52201
+ if (!config) return null;
52202
+ const IconComponent = config.Icon;
52203
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
52204
+ 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: [
52205
+ /* @__PURE__ */ jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
52206
+ /* @__PURE__ */ jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
52207
+ ] }) }),
52208
+ idleTimeVlmEnabled && confidence !== null && (() => {
52209
+ const confidencePercent = confidence * 100;
52210
+ let confidenceLabel = "Low";
52211
+ let confidenceColor = "text-red-500";
52212
+ if (confidencePercent > 95) {
52213
+ confidenceLabel = "High";
52214
+ confidenceColor = "text-green-500";
52215
+ } else if (confidencePercent >= 91) {
52216
+ confidenceLabel = "Medium";
52217
+ confidenceColor = "text-yellow-500";
52218
+ }
52219
+ 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: [
52220
+ "AI Confidence: ",
52221
+ /* @__PURE__ */ jsx("span", { className: confidenceColor, children: confidenceLabel })
52222
+ ] }) }) });
52223
+ })()
52224
+ ] });
50445
52225
  })()
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: [
52226
+ ) : /* @__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: [
52227
+ /* @__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` }),
52228
+ (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: [
52229
+ "Cycle time: ",
52230
+ currentVideo.cycle_time_seconds.toFixed(1),
52231
+ "s"
52232
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
52233
+ /* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
52234
+ /* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
52235
+ ] })
52236
+ ] }) }) : (
52237
+ /* Right side display for other video types */
52238
+ currentVideo.type === "idle_time" ? (
52239
+ // Show full colored badge for idle time
52240
+ (() => {
52241
+ const classification = getIdleTimeClassification(currentVideo);
52242
+ const confidence = getIdleTimeConfidence(currentVideo);
52243
+ const config = getRootCauseConfig(classification);
52244
+ if (!config) return null;
52245
+ const IconComponent = config.Icon;
52246
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
52247
+ 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: [
52248
+ "AI Confidence: ",
52249
+ (confidence * 100).toFixed(0),
52250
+ "%"
52251
+ ] }) }) }),
52252
+ 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: [
52253
+ /* @__PURE__ */ jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
52254
+ /* @__PURE__ */ jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
52255
+ ] }) })
52256
+ ] });
52257
+ })()
52258
+ ) : /* @__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: [
52259
+ /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${getSeverityColor(currentVideo.severity)} mr-2 animate-pulse` }),
52260
+ /* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
52261
+ currentVideo.type !== "recent_flow_red_streak" && /* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
52262
+ ] }) })
52263
+ )
52264
+ ] }) }) }) : 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..." }) }) }) }) : (
52265
+ /* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
52266
+ 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
52267
  /* @__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." })
52268
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
52269
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
50504
52270
  ] }) }) : (
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..." }) }) }) })
52271
+ /* Priority 5.5: Show "no folder selected" if activeFilter is empty */
52272
+ 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: [
52273
+ /* @__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" }) }),
52274
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Folder Selected" }),
52275
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "Please select a folder to view clips." })
52276
+ ] }) }) : (
52277
+ /* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
52278
+ 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: [
52279
+ /* @__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" }) }),
52280
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
52281
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
52282
+ ] }) }) : (
52283
+ /* Priority 7: Default loading state for any other case */
52284
+ /* @__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..." }) }) }) })
52285
+ )
52286
+ )
50507
52287
  )
50508
- )
50509
- )
50510
- ) }) }),
52288
+ ) })
52289
+ }
52290
+ ),
50511
52291
  /* @__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
52292
  /* Triage Mode - Direct tile view for cycle completions and idle time */
50513
52293
  /* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm h-full overflow-hidden flex flex-col", children: [
@@ -50642,7 +52422,7 @@ var BottlenecksContent = ({
50642
52422
  })),
50643
52423
  videos: allVideos || [],
50644
52424
  activeFilter,
50645
- currentVideoId: currentVideo?.id,
52425
+ currentVideoId: currentVideo?.id || currentClipId || void 0,
50646
52426
  counts: mergedCounts,
50647
52427
  isReady: hasInitialLoad,
50648
52428
  prefetchedPercentileCounts: isFastSlowClipFiltersEnabled ? prefetchedPercentileCounts || void 0 : void 0,
@@ -50656,11 +52436,9 @@ var BottlenecksContent = ({
50656
52436
  idleTimeVlmEnabled,
50657
52437
  showPercentileCycleFilters: isFastSlowClipFiltersEnabled,
50658
52438
  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
- },
52439
+ prefetchedClipTotals: initialTimePrefetch?.loading ? void 0 : initialTimePrefetch?.totalsByCategory,
52440
+ prefetchedPercentileClips: initialTimePrefetch?.loading ? void 0 : initialTimePrefetch?.percentileClipsByCategory,
52441
+ externallyManagedLoadingCategories,
50664
52442
  activeCategoryLoading: isCategoryLoading,
50665
52443
  idleClipSort,
50666
52444
  onIdleClipSortChange: setIdleClipSort,
@@ -70474,9 +72252,7 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
70474
72252
  await preInitializeWorkspaceDisplayNames(selectedLineId);
70475
72253
  }
70476
72254
  } else if (lineIdArray.length > 0) {
70477
- await Promise.all(
70478
- lineIdArray.map((lineId) => preInitializeWorkspaceDisplayNames(lineId))
70479
- );
72255
+ await preInitializeWorkspaceDisplayNamesForLines(lineIdArray);
70480
72256
  } else {
70481
72257
  await preInitializeWorkspaceDisplayNames();
70482
72258
  }
@@ -80940,7 +82716,6 @@ var TargetsViewUI = ({
80940
82716
  skuRequired = false,
80941
82717
  onUpdateWorkspaceSelectedSku
80942
82718
  }) => {
80943
- const { displayNames: workspaceDisplayNames } = useWorkspaceDisplayNames();
80944
82719
  const mobileMenuContext = useMobileMenu();
80945
82720
  useHideMobileHeader(!!mobileMenuContext);
80946
82721
  if (isLoading) {
@@ -81122,7 +82897,7 @@ var TargetsViewUI = ({
81122
82897
  ] })
81123
82898
  ] }) }),
81124
82899
  /* @__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);
82900
+ const formattedName = workspace.displayName || formatWorkspaceName(workspace.name, lineId);
81126
82901
  const realSkuOptions = (workspace.skuRows || []).filter((r2) => !r2.is_dummy);
81127
82902
  const showSkuDropdown = !!onUpdateWorkspaceSelectedSku && realSkuOptions.length >= 1;
81128
82903
  const isPlanLocked = !!workspace.productionPlanLock?.locked;
@@ -81673,6 +83448,7 @@ var TargetsView = ({
81673
83448
  return {
81674
83449
  id: ws.id,
81675
83450
  name: ws.workspace_id,
83451
+ displayName: ws.display_name || ws.workspace_id,
81676
83452
  targetPPH: selectedRow?.pph_threshold ?? (threshold?.pph_threshold ? Math.round(threshold.pph_threshold) : ""),
81677
83453
  targetCycleTime: selectedRow?.ideal_cycle_time ?? (threshold?.ideal_cycle_time ?? ""),
81678
83454
  targetDayOutput: selectedRow?.total_day_output ?? (threshold?.total_day_output ?? ""),
@@ -81731,6 +83507,7 @@ var TargetsView = ({
81731
83507
  return {
81732
83508
  id: ws.id,
81733
83509
  name: ws.workspace_id,
83510
+ displayName: ws.display_name || ws.workspace_id,
81734
83511
  targetPPH: "",
81735
83512
  targetCycleTime: "",
81736
83513
  targetDayOutput: "",
@@ -82211,15 +83988,31 @@ var TargetsView = ({
82211
83988
  const handleUpdateWorkspaceDisplayName = useCallback(async (workspaceId, displayName) => {
82212
83989
  try {
82213
83990
  const updated = await workspaceService.updateWorkspaceDisplayName(workspaceId, displayName);
83991
+ const nextDisplayName = updated?.display_name || displayName;
83992
+ setAllShiftsData((prev) => {
83993
+ const next = { ...prev };
83994
+ Object.entries(prev).forEach(([shiftId, shiftData]) => {
83995
+ const numericShiftId = Number(shiftId);
83996
+ const updatedShiftData = {};
83997
+ Object.entries(shiftData).forEach(([lineId, line]) => {
83998
+ updatedShiftData[lineId] = {
83999
+ ...line,
84000
+ workspaces: line.workspaces.map(
84001
+ (ws) => ws.id === workspaceId ? { ...ws, displayName: nextDisplayName } : ws
84002
+ )
84003
+ };
84004
+ });
84005
+ next[numericShiftId] = updatedShiftData;
84006
+ });
84007
+ return next;
84008
+ });
82214
84009
  if (updated?.line_id && updated?.workspace_id) {
82215
84010
  upsertWorkspaceDisplayNameInCache({
82216
84011
  lineId: updated.line_id,
82217
84012
  workspaceId: updated.workspace_id,
82218
- displayName: updated?.display_name || displayName,
84013
+ displayName: nextDisplayName,
82219
84014
  enabled: updated?.enable
82220
84015
  });
82221
- } else {
82222
- await forceRefreshWorkspaceDisplayNames();
82223
84016
  }
82224
84017
  toast.success("Workspace name updated successfully");
82225
84018
  } catch (error) {
@@ -82263,9 +84056,8 @@ var TargetsView = ({
82263
84056
  }
82264
84057
  );
82265
84058
  };
82266
- var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
82267
- var TargetsView_default = TargetsViewWithDisplayNames;
82268
- var AuthenticatedTargetsView = withAuth(React125__default.memo(TargetsViewWithDisplayNames));
84059
+ var TargetsView_default = TargetsView;
84060
+ var AuthenticatedTargetsView = withAuth(React125__default.memo(TargetsView));
82269
84061
  function useTimezone(options = {}) {
82270
84062
  const dashboardConfig = useDashboardConfig();
82271
84063
  const workspaceConfig = useWorkspaceConfig();
@@ -82445,17 +84237,22 @@ var WorkspaceHourSummaryPanel = ({
82445
84237
  return;
82446
84238
  }
82447
84239
  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);
84240
+ const summaryTimer = window.setTimeout(() => {
84241
+ void summarize({
84242
+ workspaceId,
84243
+ companyId,
84244
+ date,
84245
+ shiftId,
84246
+ hourIndex: selectedHour.hourIndex,
84247
+ hourStart: selectedHour.startTime,
84248
+ hourEnd: selectedHour.endTime,
84249
+ forceRefresh: false,
84250
+ selectionSource: selectedHour.source ?? "unknown"
84251
+ }).catch(() => void 0);
84252
+ }, 1500);
84253
+ return () => {
84254
+ window.clearTimeout(summaryTimer);
84255
+ };
82459
84256
  }, [canSummarize, companyId, date, selectedHour, selectedKey, shiftId, summarize, workspaceId]);
82460
84257
  if (!selectedHour) {
82461
84258
  return null;
@@ -82639,10 +84436,18 @@ var WorkspaceDetailView = ({
82639
84436
  setSelectedHour(null);
82640
84437
  }, [workspaceId, date, shift]);
82641
84438
  const dashboardConfig = useDashboardConfig();
84439
+ const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
82642
84440
  const { legend: efficiencyLegend } = useEfficiencyLegend();
82643
84441
  const prewarmedClipsRef = useRef(/* @__PURE__ */ new Set());
82644
84442
  const prewarmInFlightRef = useRef(/* @__PURE__ */ new Set());
82645
84443
  const [lowMomentsPrefetch, setLowMomentsPrefetch] = useState(null);
84444
+ const selectedHourClipPrefetchInFlightRef = useRef(/* @__PURE__ */ new Set());
84445
+ const selectedHourClipPrefetchAbortRef = useRef(null);
84446
+ const [initialTimePrefetch, setInitialTimePrefetch] = useState(null);
84447
+ const {
84448
+ isFastSlowClipFiltersEnabled,
84449
+ isResolved: isFastSlowClipFiltersResolved
84450
+ } = useCompanyFastSlowClipFiltersEnabled();
82646
84451
  const [aiSummaryHour, setAiSummaryHour] = useState(null);
82647
84452
  const buildHourlyOutputActionTrackingProps = useCallback((payload) => ({
82648
84453
  workspace_id: workspaceId,
@@ -82672,24 +84477,286 @@ var WorkspaceDetailView = ({
82672
84477
  timezone,
82673
84478
  workspaceId
82674
84479
  ]);
84480
+ const prefetchClipsForSelectedHour = useCallback((hour) => {
84481
+ if (!isClipsEnabled || !dashboardConfig?.s3Config || !workspaceId || !supabase) {
84482
+ return;
84483
+ }
84484
+ const resolvedDate = date || getOperationalDate(timezone);
84485
+ const resolvedShiftId = parsedShiftId ?? selectedShift;
84486
+ if (!resolvedDate || resolvedShiftId === null || resolvedShiftId === void 0) {
84487
+ return;
84488
+ }
84489
+ const categoryId = "cycle_completion";
84490
+ const regularCategoryIds = ["cycle_completion", "idle_time", "recent_flow_red_streak"];
84491
+ const percentileCategoryIds = isFastSlowClipFiltersEnabled ? ["fast-cycles", "slow-cycles"] : [];
84492
+ const allPrefetchCategoryIds = [...regularCategoryIds, ...percentileCategoryIds];
84493
+ const effectiveTimezone = hour.timezone || timezone;
84494
+ const prefetchKey = [
84495
+ workspaceId,
84496
+ resolvedDate,
84497
+ resolvedShiftId,
84498
+ hour.startTime,
84499
+ hour.endTime,
84500
+ effectiveTimezone || "",
84501
+ allPrefetchCategoryIds.join(",")
84502
+ ].join("|");
84503
+ if (initialTimePrefetch?.key === prefetchKey && !initialTimePrefetch.loading) {
84504
+ return;
84505
+ }
84506
+ if (selectedHourClipPrefetchInFlightRef.current.has(prefetchKey)) {
84507
+ return;
84508
+ }
84509
+ selectedHourClipPrefetchAbortRef.current?.abort();
84510
+ const controller = new AbortController();
84511
+ selectedHourClipPrefetchAbortRef.current = controller;
84512
+ selectedHourClipPrefetchInFlightRef.current.add(prefetchKey);
84513
+ const existingSnapshot = initialTimePrefetch?.key === prefetchKey ? initialTimePrefetch : null;
84514
+ setInitialTimePrefetch({
84515
+ key: prefetchKey,
84516
+ categoryId,
84517
+ metadata: existingSnapshot?.metadata || [],
84518
+ metadataByCategory: existingSnapshot?.metadataByCategory || {},
84519
+ totalsByCategory: existingSnapshot?.totalsByCategory || {},
84520
+ percentileClipsByCategory: existingSnapshot?.percentileClipsByCategory || {},
84521
+ firstVideo: existingSnapshot?.firstVideo || null,
84522
+ total: existingSnapshot?.total || 0,
84523
+ loading: true,
84524
+ error: null
84525
+ });
84526
+ const s3Service = videoPrefetchManager.getS3Service(dashboardConfig);
84527
+ const fetchHourlySnapshot = async () => {
84528
+ try {
84529
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
84530
+ method: "POST",
84531
+ headers: {
84532
+ "Content-Type": "application/json"
84533
+ },
84534
+ body: JSON.stringify({
84535
+ action: "hourly-snapshot",
84536
+ workspaceId,
84537
+ date: resolvedDate,
84538
+ shift: resolvedShiftId,
84539
+ startTime: hour.startTime,
84540
+ endTime: hour.endTime,
84541
+ timeFilterTimezone: effectiveTimezone
84542
+ }),
84543
+ signal: controller.signal,
84544
+ redirectReason: "session_expired"
84545
+ });
84546
+ if (!response.ok) {
84547
+ return false;
84548
+ }
84549
+ const snapshot = await response.json();
84550
+ const metadataByCategory = snapshot?.metadataByCategory && typeof snapshot.metadataByCategory === "object" ? snapshot.metadataByCategory : {};
84551
+ const percentileClipsByCategory = snapshot?.percentileClipsByCategory && typeof snapshot.percentileClipsByCategory === "object" ? snapshot.percentileClipsByCategory : {};
84552
+ const totalsByCategory = snapshot?.totalsByCategory && typeof snapshot.totalsByCategory === "object" ? Object.entries(snapshot.totalsByCategory).reduce((accumulator, [key, value]) => {
84553
+ const total = Number(value);
84554
+ accumulator[key] = Number.isFinite(total) ? Math.max(0, total) : 0;
84555
+ return accumulator;
84556
+ }, {}) : {};
84557
+ const snapshotCategoryId = typeof snapshot?.categoryId === "string" ? snapshot.categoryId : categoryId;
84558
+ const metadata = Array.isArray(snapshot?.metadata) ? snapshot.metadata : Array.isArray(metadataByCategory[snapshotCategoryId]) ? metadataByCategory[snapshotCategoryId] : [];
84559
+ if (controller.signal.aborted) {
84560
+ return true;
84561
+ }
84562
+ setInitialTimePrefetch({
84563
+ key: prefetchKey,
84564
+ categoryId: snapshotCategoryId,
84565
+ metadata,
84566
+ metadataByCategory,
84567
+ totalsByCategory,
84568
+ percentileClipsByCategory,
84569
+ firstVideo: snapshot?.firstVideo || null,
84570
+ total: typeof totalsByCategory[snapshotCategoryId] === "number" ? totalsByCategory[snapshotCategoryId] : metadata.length,
84571
+ loading: false,
84572
+ error: null
84573
+ });
84574
+ return true;
84575
+ } catch (error2) {
84576
+ if (error2.name === "AbortError") {
84577
+ return true;
84578
+ }
84579
+ console.warn("[WorkspaceDetailView] Hourly clips snapshot failed; falling back to category prefetch:", error2);
84580
+ return false;
84581
+ }
84582
+ };
84583
+ const fetchRegularCategory = async (prefetchCategoryId) => {
84584
+ const metadataResponse = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
84585
+ method: "POST",
84586
+ headers: {
84587
+ "Content-Type": "application/json"
84588
+ },
84589
+ body: JSON.stringify({
84590
+ action: "clip-metadata",
84591
+ workspaceId,
84592
+ date: resolvedDate,
84593
+ shift: resolvedShiftId,
84594
+ category: prefetchCategoryId,
84595
+ page: 1,
84596
+ limit: 500,
84597
+ knownTotal: null,
84598
+ startTime: hour.startTime,
84599
+ endTime: hour.endTime,
84600
+ timeFilterTimezone: effectiveTimezone,
84601
+ sort: prefetchCategoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : "latest"
84602
+ }),
84603
+ signal: controller.signal,
84604
+ redirectReason: "session_expired"
84605
+ });
84606
+ if (!metadataResponse.ok) {
84607
+ throw new Error(`Hourly clips metadata prefetch failed for ${prefetchCategoryId}: ${metadataResponse.status}`);
84608
+ }
84609
+ const metadataData = await metadataResponse.json();
84610
+ return {
84611
+ categoryId: prefetchCategoryId,
84612
+ clips: Array.isArray(metadataData?.clips) ? metadataData.clips : [],
84613
+ total: typeof metadataData?.total === "number" ? metadataData.total : 0
84614
+ };
84615
+ };
84616
+ const fetchPercentileCategory = async (prefetchCategoryId) => {
84617
+ const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
84618
+ method: "POST",
84619
+ headers: {
84620
+ "Content-Type": "application/json"
84621
+ },
84622
+ body: JSON.stringify({
84623
+ action: "percentile-clips",
84624
+ workspaceId,
84625
+ startDate: `${resolvedDate}T00:00:00Z`,
84626
+ endDate: `${resolvedDate}T23:59:59Z`,
84627
+ percentile: 10,
84628
+ shiftId: resolvedShiftId,
84629
+ limit: 500,
84630
+ startTime: hour.startTime,
84631
+ endTime: hour.endTime,
84632
+ timeFilterTimezone: effectiveTimezone,
84633
+ percentileAction: prefetchCategoryId
84634
+ }),
84635
+ signal: controller.signal,
84636
+ redirectReason: "session_expired"
84637
+ });
84638
+ if (!response.ok) {
84639
+ throw new Error(`Hourly percentile clips prefetch failed for ${prefetchCategoryId}: ${response.status}`);
84640
+ }
84641
+ const data = await response.json();
84642
+ return {
84643
+ categoryId: prefetchCategoryId,
84644
+ clips: Array.isArray(data?.clips) ? data.clips : [],
84645
+ total: typeof data?.total === "number" ? data.total : 0
84646
+ };
84647
+ };
84648
+ const runPrefetch = async () => {
84649
+ try {
84650
+ const snapshotLoaded = await fetchHourlySnapshot();
84651
+ if (snapshotLoaded) {
84652
+ return;
84653
+ }
84654
+ const cycleCompletionPromise = fetchRegularCategory("cycle_completion");
84655
+ let resolvedFirstVideo = null;
84656
+ const firstVideoPromise = cycleCompletionPromise.then(async (cycleResponse) => {
84657
+ const firstClipId = cycleResponse.clips[0]?.clipId || cycleResponse.clips[0]?.id || null;
84658
+ if (!firstClipId) {
84659
+ return null;
84660
+ }
84661
+ const video = await s3Service.getClipById(firstClipId);
84662
+ resolvedFirstVideo = video;
84663
+ if (!controller.signal.aborted) {
84664
+ setInitialTimePrefetch((prev) => prev?.key === prefetchKey ? {
84665
+ ...prev,
84666
+ firstVideo: video
84667
+ } : prev);
84668
+ }
84669
+ return video;
84670
+ }).catch((error2) => {
84671
+ if (error2.name !== "AbortError") {
84672
+ console.warn("[WorkspaceDetailView] Hourly first clip prefetch failed:", error2);
84673
+ }
84674
+ return null;
84675
+ });
84676
+ const percentileResponsesPromise = Promise.all(percentileCategoryIds.map(fetchPercentileCategory));
84677
+ const regularResponses = await Promise.all([
84678
+ cycleCompletionPromise,
84679
+ fetchRegularCategory("idle_time"),
84680
+ fetchRegularCategory("recent_flow_red_streak")
84681
+ ]);
84682
+ const percentileResponses = await percentileResponsesPromise;
84683
+ const metadataByCategory = regularResponses.reduce((accumulator, response) => {
84684
+ accumulator[response.categoryId] = response.clips;
84685
+ return accumulator;
84686
+ }, {});
84687
+ const percentileClipsByCategory = percentileResponses.reduce((accumulator, response) => {
84688
+ accumulator[response.categoryId] = response.clips;
84689
+ return accumulator;
84690
+ }, {});
84691
+ const totalsByCategory = [...regularResponses, ...percentileResponses].reduce((accumulator, response) => {
84692
+ accumulator[response.categoryId] = Math.max(0, Number(response.total || 0));
84693
+ return accumulator;
84694
+ }, {});
84695
+ const metadata = metadataByCategory[categoryId] || [];
84696
+ if (controller.signal.aborted) {
84697
+ return;
84698
+ }
84699
+ setInitialTimePrefetch({
84700
+ key: prefetchKey,
84701
+ categoryId,
84702
+ metadata,
84703
+ metadataByCategory,
84704
+ totalsByCategory,
84705
+ percentileClipsByCategory,
84706
+ firstVideo: resolvedFirstVideo,
84707
+ total: typeof totalsByCategory[categoryId] === "number" ? totalsByCategory[categoryId] : metadata.length,
84708
+ loading: false,
84709
+ error: null
84710
+ });
84711
+ void firstVideoPromise;
84712
+ } catch (error2) {
84713
+ if (error2.name === "AbortError") {
84714
+ return;
84715
+ }
84716
+ console.warn("[WorkspaceDetailView] Hourly clips prefetch failed:", error2);
84717
+ setInitialTimePrefetch((prev) => prev?.key === prefetchKey ? {
84718
+ ...prev,
84719
+ loading: false,
84720
+ error: error2 instanceof Error ? error2.message : "Hourly clips prefetch failed"
84721
+ } : prev);
84722
+ } finally {
84723
+ selectedHourClipPrefetchInFlightRef.current.delete(prefetchKey);
84724
+ }
84725
+ };
84726
+ void runPrefetch();
84727
+ }, [
84728
+ dashboardConfig,
84729
+ date,
84730
+ initialTimePrefetch,
84731
+ isFastSlowClipFiltersEnabled,
84732
+ isClipsEnabled,
84733
+ parsedShiftId,
84734
+ selectedShift,
84735
+ supabase,
84736
+ timezone,
84737
+ workspaceId
84738
+ ]);
82675
84739
  const handleOutputHourSelect = useCallback((payload) => {
82676
- setSelectedHour({
84740
+ const hour = {
82677
84741
  source: "output",
82678
84742
  hourIndex: payload.hourIndex,
82679
84743
  timeRange: payload.timeRange,
82680
84744
  startTime: payload.startTime,
82681
84745
  endTime: payload.endTime,
84746
+ timezone: payload.timezone,
82682
84747
  status: payload.status,
82683
84748
  output: payload.output,
82684
84749
  target: payload.target
82685
- });
82686
- }, []);
84750
+ };
84751
+ setSelectedHour(hour);
84752
+ prefetchClipsForSelectedHour(hour);
84753
+ }, [prefetchClipsForSelectedHour]);
82687
84754
  const handleAiSummaryClick = useCallback((payload) => {
82688
84755
  trackCoreEvent("Hourly Output Ask AI Clicked", buildHourlyOutputActionTrackingProps(payload));
82689
84756
  setAiSummaryHour(payload);
82690
84757
  }, [buildHourlyOutputActionTrackingProps]);
82691
84758
  const handleCycleHourSelect = useCallback((payload) => {
82692
- setSelectedHour({
84759
+ const hour = {
82693
84760
  source: "cycle",
82694
84761
  hourIndex: payload.hourIndex,
82695
84762
  timeRange: payload.timeRange,
@@ -82699,17 +84766,23 @@ var WorkspaceDetailView = ({
82699
84766
  cycleTime: payload.cycleTime,
82700
84767
  idealCycleTime: payload.idealCycleTime,
82701
84768
  idleMinutes: payload.idleMinutes
82702
- });
82703
- }, []);
84769
+ };
84770
+ setSelectedHour(hour);
84771
+ prefetchClipsForSelectedHour(hour);
84772
+ }, [prefetchClipsForSelectedHour]);
82704
84773
  const handleOpenClipsForHour = useCallback((hour) => {
84774
+ prefetchClipsForSelectedHour(hour);
82705
84775
  setPendingClipHourFilter({
82706
84776
  startTime: hour.startTime,
82707
84777
  endTime: hour.endTime,
82708
84778
  sourceLabel: hour.timeRange,
82709
- status: hour.status === "below_target" || hour.status === "above_standard" ? "below_target" : "met_target"
84779
+ status: hour.status === "below_target" || hour.status === "above_standard" ? "below_target" : "met_target",
84780
+ timezone: hour.timezone,
84781
+ categoryId: "cycle_completion",
84782
+ categoryIds: ["cycle_completion", "fast-cycles", "slow-cycles", "idle_time", "recent_flow_red_streak"]
82710
84783
  });
82711
84784
  setActiveTab("bottlenecks");
82712
- }, []);
84785
+ }, [prefetchClipsForSelectedHour]);
82713
84786
  const handleWatchClipsFromChart = useCallback((payload) => {
82714
84787
  trackCoreEvent("Hourly Output Watch Clips Clicked", buildHourlyOutputActionTrackingProps(payload));
82715
84788
  handleOpenClipsForHour({
@@ -82718,6 +84791,7 @@ var WorkspaceDetailView = ({
82718
84791
  timeRange: payload.timeRange,
82719
84792
  startTime: payload.startTime,
82720
84793
  endTime: payload.endTime,
84794
+ timezone: payload.timezone,
82721
84795
  status: payload.status,
82722
84796
  output: payload.output,
82723
84797
  target: payload.target
@@ -82740,11 +84814,6 @@ var WorkspaceDetailView = ({
82740
84814
  startDate: isFullRange ? void 0 : rangeStart,
82741
84815
  endDate: isFullRange ? void 0 : rangeEnd
82742
84816
  });
82743
- const {
82744
- isFastSlowClipFiltersEnabled,
82745
- isResolved: isFastSlowClipFiltersResolved
82746
- } = useCompanyFastSlowClipFiltersEnabled();
82747
- const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
82748
84817
  dashboardConfig?.supervisorConfig?.enabled || false;
82749
84818
  const routedLineId = lineId || selectedLineId;
82750
84819
  const latestCachedDetailedMetrics = useMemo(() => {
@@ -84491,6 +86560,7 @@ var WorkspaceDetailView = ({
84491
86560
  workspaceMetrics: detailedWorkspaceMetrics || void 0,
84492
86561
  prefetchedPercentileCounts: isFastSlowClipFiltersEnabled ? prefetchedPercentileCounts : null,
84493
86562
  lowMomentsPrefetch,
86563
+ initialTimePrefetch,
84494
86564
  initialTimeFilter: pendingClipHourFilter,
84495
86565
  className: "h-[calc(100vh-10rem)]"
84496
86566
  }
@@ -94048,4 +96118,6 @@ var RecentFlowSnapshotGrid = ({
94048
96118
  );
94049
96119
  };
94050
96120
 
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 };
96121
+ 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 };
96122
+ //# sourceMappingURL=index.mjs.map
96123
+ //# sourceMappingURL=index.mjs.map