@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/automation.js +2 -0
- package/dist/automation.js.map +1 -0
- package/dist/automation.mjs +2 -0
- package/dist/automation.mjs.map +1 -0
- package/dist/index.css +1 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +73 -33
- package/dist/index.d.ts +73 -33
- package/dist/index.js +2621 -548
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2622 -550
- package/dist/index.mjs.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6067,7 +6067,8 @@ var getAuthToken2 = async () => {
|
|
|
6067
6067
|
var workspaceService = {
|
|
6068
6068
|
// Cache for workspace display names to avoid repeated API calls
|
|
6069
6069
|
_workspaceDisplayNamesCache: /* @__PURE__ */ new Map(),
|
|
6070
|
-
|
|
6070
|
+
_workspaceDisplayNamesInFlight: /* @__PURE__ */ new Map(),
|
|
6071
|
+
_workspaceDisplayNamesByLineInFlight: /* @__PURE__ */ new Map(),
|
|
6071
6072
|
_cacheExpiryMs: 5 * 60 * 1e3,
|
|
6072
6073
|
// 5 minutes cache
|
|
6073
6074
|
// Cache for workspace lists to avoid repeated API calls (line configuration changes infrequently)
|
|
@@ -6326,12 +6327,74 @@ var workspaceService = {
|
|
|
6326
6327
|
* Returns a map of workspace_id -> display_name
|
|
6327
6328
|
*/
|
|
6328
6329
|
async getWorkspaceDisplayNames(companyId, lineId) {
|
|
6329
|
-
|
|
6330
|
+
const cacheKey = `${companyId || "all"}::${lineId || "all"}`;
|
|
6331
|
+
const existingInFlight = this._workspaceDisplayNamesInFlight.get(cacheKey);
|
|
6332
|
+
if (existingInFlight) {
|
|
6333
|
+
return existingInFlight;
|
|
6334
|
+
}
|
|
6335
|
+
const fetchPromise = (async () => {
|
|
6336
|
+
try {
|
|
6337
|
+
const token = await getAuthToken2();
|
|
6338
|
+
const apiUrl = getBackendUrl2();
|
|
6339
|
+
const params = new URLSearchParams();
|
|
6340
|
+
if (companyId) params.append("company_id", companyId);
|
|
6341
|
+
if (lineId) params.append("line_id", lineId);
|
|
6342
|
+
const response = await fetch(`${apiUrl}/api/workspaces/display-names?${params.toString()}`, {
|
|
6343
|
+
headers: {
|
|
6344
|
+
"Authorization": `Bearer ${token}`,
|
|
6345
|
+
"Content-Type": "application/json"
|
|
6346
|
+
}
|
|
6347
|
+
});
|
|
6348
|
+
if (!response.ok) {
|
|
6349
|
+
const errorText = await response.text();
|
|
6350
|
+
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
6351
|
+
}
|
|
6352
|
+
const data = await response.json();
|
|
6353
|
+
const displayNamesMap = /* @__PURE__ */ new Map();
|
|
6354
|
+
if (data.display_names) {
|
|
6355
|
+
Object.entries(data.display_names).forEach(([key, value]) => {
|
|
6356
|
+
displayNamesMap.set(key, value);
|
|
6357
|
+
});
|
|
6358
|
+
}
|
|
6359
|
+
this._workspaceDisplayNamesCache.set(cacheKey, {
|
|
6360
|
+
displayNames: displayNamesMap,
|
|
6361
|
+
timestamp: Date.now()
|
|
6362
|
+
});
|
|
6363
|
+
return displayNamesMap;
|
|
6364
|
+
} catch (error) {
|
|
6365
|
+
console.error("Error fetching workspace display names:", error);
|
|
6366
|
+
addSentryBreadcrumb("Workspace display-name fallback failed", {
|
|
6367
|
+
surface: "workspace_display_names",
|
|
6368
|
+
route: "/api/workspaces/display-names",
|
|
6369
|
+
severity: "warning",
|
|
6370
|
+
extras: {
|
|
6371
|
+
company_id: companyId || null,
|
|
6372
|
+
line_id: lineId || null,
|
|
6373
|
+
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
6374
|
+
error_message: error instanceof Error ? error.message : String(error)
|
|
6375
|
+
}
|
|
6376
|
+
});
|
|
6377
|
+
throw error;
|
|
6378
|
+
} finally {
|
|
6379
|
+
this._workspaceDisplayNamesInFlight.delete(cacheKey);
|
|
6380
|
+
}
|
|
6381
|
+
})();
|
|
6382
|
+
this._workspaceDisplayNamesInFlight.set(cacheKey, fetchPromise);
|
|
6383
|
+
return fetchPromise;
|
|
6384
|
+
},
|
|
6385
|
+
async getWorkspaceDisplayNamesByLine(companyId, lineIds) {
|
|
6386
|
+
const normalizedLineIds = Array.from(new Set((lineIds || []).filter(Boolean))).sort();
|
|
6387
|
+
const cacheKey = `${companyId || "all"}::byLine::${normalizedLineIds.join(",") || "all"}`;
|
|
6388
|
+
const existingInFlight = this._workspaceDisplayNamesByLineInFlight.get(cacheKey);
|
|
6389
|
+
if (existingInFlight) {
|
|
6390
|
+
return existingInFlight;
|
|
6391
|
+
}
|
|
6392
|
+
const fetchPromise = (async () => {
|
|
6330
6393
|
const token = await getAuthToken2();
|
|
6331
6394
|
const apiUrl = getBackendUrl2();
|
|
6332
6395
|
const params = new URLSearchParams();
|
|
6333
6396
|
if (companyId) params.append("company_id", companyId);
|
|
6334
|
-
if (
|
|
6397
|
+
if (normalizedLineIds.length > 0) params.append("line_ids", normalizedLineIds.join(","));
|
|
6335
6398
|
const response = await fetch(`${apiUrl}/api/workspaces/display-names?${params.toString()}`, {
|
|
6336
6399
|
headers: {
|
|
6337
6400
|
"Authorization": `Bearer ${token}`,
|
|
@@ -6343,39 +6406,32 @@ var workspaceService = {
|
|
|
6343
6406
|
throw new Error(`Backend API error (${response.status}): ${errorText}`);
|
|
6344
6407
|
}
|
|
6345
6408
|
const data = await response.json();
|
|
6346
|
-
const
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6409
|
+
const source = data.display_names_by_line || {};
|
|
6410
|
+
const byLine = /* @__PURE__ */ new Map();
|
|
6411
|
+
Object.entries(source).forEach(([lineId, displayNames]) => {
|
|
6412
|
+
if (normalizedLineIds.length > 0 && !normalizedLineIds.includes(lineId)) return;
|
|
6413
|
+
const lineMap = /* @__PURE__ */ new Map();
|
|
6414
|
+
Object.entries(displayNames).forEach(([workspaceId, displayName]) => {
|
|
6415
|
+
lineMap.set(workspaceId, displayName);
|
|
6350
6416
|
});
|
|
6351
|
-
|
|
6352
|
-
this._workspaceDisplayNamesCache = displayNamesMap;
|
|
6353
|
-
this._cacheTimestamp = Date.now();
|
|
6354
|
-
return displayNamesMap;
|
|
6355
|
-
} catch (error) {
|
|
6356
|
-
console.error("Error fetching workspace display names:", error);
|
|
6357
|
-
addSentryBreadcrumb("Workspace display-name fallback failed", {
|
|
6358
|
-
surface: "workspace_display_names",
|
|
6359
|
-
route: "/api/workspaces/display-names",
|
|
6360
|
-
severity: "warning",
|
|
6361
|
-
extras: {
|
|
6362
|
-
company_id: companyId || null,
|
|
6363
|
-
line_id: lineId || null,
|
|
6364
|
-
pathname: typeof window !== "undefined" ? window.location.pathname : "unknown",
|
|
6365
|
-
error_message: error instanceof Error ? error.message : String(error)
|
|
6366
|
-
}
|
|
6417
|
+
byLine.set(lineId, lineMap);
|
|
6367
6418
|
});
|
|
6368
|
-
|
|
6369
|
-
}
|
|
6419
|
+
return byLine;
|
|
6420
|
+
})().finally(() => {
|
|
6421
|
+
this._workspaceDisplayNamesByLineInFlight.delete(cacheKey);
|
|
6422
|
+
});
|
|
6423
|
+
this._workspaceDisplayNamesByLineInFlight.set(cacheKey, fetchPromise);
|
|
6424
|
+
return fetchPromise;
|
|
6370
6425
|
},
|
|
6371
6426
|
/**
|
|
6372
6427
|
* Gets cached workspace display names (with cache expiry)
|
|
6373
6428
|
*/
|
|
6374
6429
|
async getCachedWorkspaceDisplayNames(companyId, lineId) {
|
|
6375
6430
|
const now4 = Date.now();
|
|
6376
|
-
const
|
|
6377
|
-
|
|
6378
|
-
|
|
6431
|
+
const cacheKey = `${companyId || "all"}::${lineId || "all"}`;
|
|
6432
|
+
const cached = this._workspaceDisplayNamesCache.get(cacheKey);
|
|
6433
|
+
if (cached && now4 - cached.timestamp < this._cacheExpiryMs && cached.displayNames.size > 0) {
|
|
6434
|
+
return cached.displayNames;
|
|
6379
6435
|
}
|
|
6380
6436
|
return this.getWorkspaceDisplayNames(companyId, lineId);
|
|
6381
6437
|
},
|
|
@@ -6396,7 +6452,8 @@ var workspaceService = {
|
|
|
6396
6452
|
*/
|
|
6397
6453
|
clearWorkspaceDisplayNamesCache() {
|
|
6398
6454
|
this._workspaceDisplayNamesCache.clear();
|
|
6399
|
-
this.
|
|
6455
|
+
this._workspaceDisplayNamesInFlight.clear();
|
|
6456
|
+
this._workspaceDisplayNamesByLineInFlight.clear();
|
|
6400
6457
|
},
|
|
6401
6458
|
clearWorkspacesCache() {
|
|
6402
6459
|
this._workspacesCache.clear();
|
|
@@ -6902,10 +6959,16 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
6902
6959
|
hasBackendUrl() {
|
|
6903
6960
|
return Boolean(process.env.NEXT_PUBLIC_BACKEND_URL);
|
|
6904
6961
|
}
|
|
6962
|
+
isCanonicalDate(value) {
|
|
6963
|
+
return typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value);
|
|
6964
|
+
}
|
|
6905
6965
|
async fetchBackendWorkspaceUptimeSummaries(companyId, lineIds, date, shiftId, timezone) {
|
|
6906
6966
|
if (!this.hasBackendUrl()) {
|
|
6907
6967
|
return null;
|
|
6908
6968
|
}
|
|
6969
|
+
if (!this.isCanonicalDate(date)) {
|
|
6970
|
+
return null;
|
|
6971
|
+
}
|
|
6909
6972
|
const supabase = _getSupabaseInstance();
|
|
6910
6973
|
if (!supabase) throw new Error("Supabase client not initialized");
|
|
6911
6974
|
const params = new URLSearchParams({
|
|
@@ -6955,6 +7018,9 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
6955
7018
|
if (!this.hasBackendUrl()) {
|
|
6956
7019
|
return null;
|
|
6957
7020
|
}
|
|
7021
|
+
if (!this.isCanonicalDate(date)) {
|
|
7022
|
+
return null;
|
|
7023
|
+
}
|
|
6958
7024
|
const supabase = _getSupabaseInstance();
|
|
6959
7025
|
if (!supabase) throw new Error("Supabase client not initialized");
|
|
6960
7026
|
const params = new URLSearchParams({
|
|
@@ -7689,6 +7755,24 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
7689
7755
|
const currentTiming = this.getShiftTiming(timezone, shiftConfig);
|
|
7690
7756
|
const queryDate = overrideDate ?? currentTiming.date;
|
|
7691
7757
|
const queryShiftId = overrideShiftId ?? currentTiming.shiftId;
|
|
7758
|
+
if (!this.isCanonicalDate(queryDate)) {
|
|
7759
|
+
return {
|
|
7760
|
+
shiftId: queryShiftId,
|
|
7761
|
+
shiftLabel: currentTiming.shiftLabel,
|
|
7762
|
+
shiftStart: dateFnsTz.formatInTimeZone(currentTiming.shiftStartDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX"),
|
|
7763
|
+
shiftEnd: dateFnsTz.formatInTimeZone(currentTiming.shiftEndDate, timezone, "yyyy-MM-dd'T'HH:mm:ssXXX"),
|
|
7764
|
+
totalMinutes: currentTiming.totalMinutes,
|
|
7765
|
+
completedMinutes: currentTiming.completedMinutes,
|
|
7766
|
+
uptimeMinutes: 0,
|
|
7767
|
+
downtimeMinutes: 0,
|
|
7768
|
+
pendingMinutes: currentTiming.pendingMinutes,
|
|
7769
|
+
uptimePercentage: 0,
|
|
7770
|
+
hasData: false,
|
|
7771
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7772
|
+
points: [],
|
|
7773
|
+
downtimeSegments: []
|
|
7774
|
+
};
|
|
7775
|
+
}
|
|
7692
7776
|
if (lineId) {
|
|
7693
7777
|
const backendTimeline = await this.fetchBackendWorkspaceUptimeTimeline(
|
|
7694
7778
|
workspaceId,
|
|
@@ -7911,6 +7995,9 @@ var WorkspaceHealthService = class _WorkspaceHealthService {
|
|
|
7911
7995
|
const defaultTimezone = dashboardConfig?.dateTimeConfig?.defaultTimezone || "UTC";
|
|
7912
7996
|
const shiftConfig = passedShiftConfig || dashboardConfig?.shiftConfig;
|
|
7913
7997
|
const effectiveTimezone = timezone || shiftConfig?.timezone || defaultTimezone;
|
|
7998
|
+
if (overrideDate !== void 0 && !this.isCanonicalDate(overrideDate)) {
|
|
7999
|
+
return /* @__PURE__ */ new Map();
|
|
8000
|
+
}
|
|
7914
8001
|
if (lineShiftConfigs && lineShiftConfigs.size > 0) {
|
|
7915
8002
|
return this.calculateWorkspaceUptimeMultiLine(
|
|
7916
8003
|
companyId,
|
|
@@ -12003,85 +12090,290 @@ function formatReasonLabel(reason) {
|
|
|
12003
12090
|
return reason.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
12004
12091
|
}
|
|
12005
12092
|
|
|
12093
|
+
// src/lib/services/sessionActivityAccumulator.ts
|
|
12094
|
+
var TRACKED_ACTIVITY_EVENTS = ["mousemove", "pointermove", "keydown", "click", "scroll", "wheel", "touchstart"];
|
|
12095
|
+
function createSessionData(sessionId, userId, companyId, now4, isVisible, isFocused) {
|
|
12096
|
+
return {
|
|
12097
|
+
sessionId,
|
|
12098
|
+
userId,
|
|
12099
|
+
companyId,
|
|
12100
|
+
startedAt: now4,
|
|
12101
|
+
lastTick: now4,
|
|
12102
|
+
cumulativeTotalMs: 0,
|
|
12103
|
+
cumulativeActiveMs: 0,
|
|
12104
|
+
cumulativePassiveMs: 0,
|
|
12105
|
+
lastActivityAt: null,
|
|
12106
|
+
isUserActive: false,
|
|
12107
|
+
isVisible,
|
|
12108
|
+
isFocused,
|
|
12109
|
+
state: "ACTIVE",
|
|
12110
|
+
currentSegment: null,
|
|
12111
|
+
pendingSegments: [],
|
|
12112
|
+
segmentCounter: 0
|
|
12113
|
+
};
|
|
12114
|
+
}
|
|
12115
|
+
var SessionActivityAccumulator = class {
|
|
12116
|
+
constructor(idleThresholdMs, getDashboardView) {
|
|
12117
|
+
this.idleThresholdMs = idleThresholdMs;
|
|
12118
|
+
this.getDashboardView = getDashboardView;
|
|
12119
|
+
}
|
|
12120
|
+
shouldTrackTime(session) {
|
|
12121
|
+
if (!session) return false;
|
|
12122
|
+
return session.state === "ACTIVE" && session.isVisible && session.isFocused;
|
|
12123
|
+
}
|
|
12124
|
+
getActivityState(session, now4) {
|
|
12125
|
+
if (!session.lastActivityAt) {
|
|
12126
|
+
return "passive";
|
|
12127
|
+
}
|
|
12128
|
+
const timeSinceActivity = now4.getTime() - session.lastActivityAt.getTime();
|
|
12129
|
+
return timeSinceActivity <= this.idleThresholdMs ? "active" : "passive";
|
|
12130
|
+
}
|
|
12131
|
+
updateTimes(session, now4 = /* @__PURE__ */ new Date()) {
|
|
12132
|
+
if (!session || session.state === "ENDED") return;
|
|
12133
|
+
const elapsed = now4.getTime() - session.lastTick.getTime();
|
|
12134
|
+
if (elapsed <= 0) return;
|
|
12135
|
+
if (!this.shouldTrackTime(session)) {
|
|
12136
|
+
this.flushCurrentSegment(session, now4);
|
|
12137
|
+
session.lastTick = now4;
|
|
12138
|
+
return;
|
|
12139
|
+
}
|
|
12140
|
+
let cursor = new Date(session.lastTick);
|
|
12141
|
+
const idleCutoff = session.lastActivityAt ? new Date(session.lastActivityAt.getTime() + this.idleThresholdMs) : null;
|
|
12142
|
+
if (idleCutoff && cursor < idleCutoff && now4 > idleCutoff) {
|
|
12143
|
+
this.addTrackedDuration(session, "active", cursor, idleCutoff);
|
|
12144
|
+
cursor = idleCutoff;
|
|
12145
|
+
}
|
|
12146
|
+
this.addTrackedDuration(session, this.getActivityState(session, now4), cursor, now4);
|
|
12147
|
+
session.lastTick = now4;
|
|
12148
|
+
}
|
|
12149
|
+
flushCurrentSegment(session, endAt) {
|
|
12150
|
+
if (!session?.currentSegment) return;
|
|
12151
|
+
const current = session.currentSegment;
|
|
12152
|
+
const currentEndMs = new Date(current.segment_end_at).getTime();
|
|
12153
|
+
if (endAt.getTime() > currentEndMs) {
|
|
12154
|
+
current.duration_ms += endAt.getTime() - currentEndMs;
|
|
12155
|
+
current.segment_end_at = endAt.toISOString();
|
|
12156
|
+
}
|
|
12157
|
+
if (current.duration_ms > 0) {
|
|
12158
|
+
session.pendingSegments.push(current);
|
|
12159
|
+
}
|
|
12160
|
+
session.currentSegment = null;
|
|
12161
|
+
}
|
|
12162
|
+
addTrackedDuration(session, state, start, end) {
|
|
12163
|
+
const durationMs = end.getTime() - start.getTime();
|
|
12164
|
+
if (durationMs <= 0) return;
|
|
12165
|
+
session.cumulativeTotalMs += durationMs;
|
|
12166
|
+
if (state === "active") {
|
|
12167
|
+
session.cumulativeActiveMs += durationMs;
|
|
12168
|
+
session.isUserActive = true;
|
|
12169
|
+
} else {
|
|
12170
|
+
session.cumulativePassiveMs += durationMs;
|
|
12171
|
+
session.isUserActive = false;
|
|
12172
|
+
}
|
|
12173
|
+
this.addSegmentDuration(session, state, start, end);
|
|
12174
|
+
}
|
|
12175
|
+
addSegmentDuration(session, state, start, end) {
|
|
12176
|
+
const durationMs = end.getTime() - start.getTime();
|
|
12177
|
+
if (durationMs <= 0) return;
|
|
12178
|
+
const dashboardView = this.getDashboardView();
|
|
12179
|
+
const current = session.currentSegment;
|
|
12180
|
+
if (current && current.state === state && current.dashboard_view === dashboardView && current.segment_end_at === start.toISOString()) {
|
|
12181
|
+
current.segment_end_at = end.toISOString();
|
|
12182
|
+
current.duration_ms += durationMs;
|
|
12183
|
+
return;
|
|
12184
|
+
}
|
|
12185
|
+
this.flushCurrentSegment(session, start);
|
|
12186
|
+
session.segmentCounter += 1;
|
|
12187
|
+
session.currentSegment = {
|
|
12188
|
+
client_segment_id: `${session.sessionId}_seg_${session.segmentCounter}`,
|
|
12189
|
+
segment_start_at: start.toISOString(),
|
|
12190
|
+
segment_end_at: end.toISOString(),
|
|
12191
|
+
state,
|
|
12192
|
+
duration_ms: durationMs,
|
|
12193
|
+
dashboard_view: dashboardView,
|
|
12194
|
+
tracker_version: 2
|
|
12195
|
+
};
|
|
12196
|
+
}
|
|
12197
|
+
};
|
|
12198
|
+
|
|
12199
|
+
// src/lib/services/sessionTrackerTransport.ts
|
|
12200
|
+
var SessionTrackerTransport = class {
|
|
12201
|
+
constructor(config, trackerVersion, maxSegmentsPerRequest) {
|
|
12202
|
+
this.config = config;
|
|
12203
|
+
this.trackerVersion = trackerVersion;
|
|
12204
|
+
this.maxSegmentsPerRequest = maxSegmentsPerRequest;
|
|
12205
|
+
}
|
|
12206
|
+
async sendStartSession(session) {
|
|
12207
|
+
const token = await this.config.getAccessToken();
|
|
12208
|
+
if (!token) {
|
|
12209
|
+
console.warn("[SessionTracker] No access token, skipping start session API call");
|
|
12210
|
+
return;
|
|
12211
|
+
}
|
|
12212
|
+
await fetch(`${this.config.apiBaseUrl}/api/sessions/start`, {
|
|
12213
|
+
method: "POST",
|
|
12214
|
+
headers: this.authHeaders(token),
|
|
12215
|
+
body: JSON.stringify({
|
|
12216
|
+
session_id: session.sessionId,
|
|
12217
|
+
user_agent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
|
|
12218
|
+
dashboard_view: this.getDashboardView(),
|
|
12219
|
+
tracker_version: this.trackerVersion
|
|
12220
|
+
})
|
|
12221
|
+
});
|
|
12222
|
+
}
|
|
12223
|
+
async sendHeartbeat(session) {
|
|
12224
|
+
const token = await this.config.getAccessToken();
|
|
12225
|
+
if (!token) return null;
|
|
12226
|
+
const segmentsToSend = session.pendingSegments.slice(0, this.maxSegmentsPerRequest);
|
|
12227
|
+
const sentSegmentIds = new Set(segmentsToSend.map((segment) => segment.client_segment_id));
|
|
12228
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/sessions/heartbeat`, {
|
|
12229
|
+
method: "POST",
|
|
12230
|
+
headers: this.authHeaders(token),
|
|
12231
|
+
body: JSON.stringify({
|
|
12232
|
+
session_id: session.sessionId,
|
|
12233
|
+
cumulative_total_ms: session.cumulativeTotalMs,
|
|
12234
|
+
cumulative_active_ms: session.cumulativeActiveMs,
|
|
12235
|
+
cumulative_passive_ms: session.cumulativePassiveMs,
|
|
12236
|
+
visibility_state: typeof document !== "undefined" ? document.visibilityState : "visible",
|
|
12237
|
+
is_user_active: session.isUserActive,
|
|
12238
|
+
dashboard_view: this.getDashboardView(),
|
|
12239
|
+
tracker_version: this.trackerVersion,
|
|
12240
|
+
activity_segments: segmentsToSend
|
|
12241
|
+
})
|
|
12242
|
+
});
|
|
12243
|
+
const payload = await response.json().catch(() => ({}));
|
|
12244
|
+
return {
|
|
12245
|
+
ok: response.ok,
|
|
12246
|
+
acknowledged: payload?.acknowledged === false ? false : payload?.acknowledged === true ? true : null,
|
|
12247
|
+
sentSegmentIds
|
|
12248
|
+
};
|
|
12249
|
+
}
|
|
12250
|
+
async sendEndSession(session, reason, keepalive) {
|
|
12251
|
+
const token = keepalive && this.config.getCachedAccessToken ? this.config.getCachedAccessToken() : await this.config.getAccessToken();
|
|
12252
|
+
if (!token) {
|
|
12253
|
+
console.warn("[SessionTracker] No access token, skipping end session API call");
|
|
12254
|
+
return false;
|
|
12255
|
+
}
|
|
12256
|
+
const response = await fetch(`${this.config.apiBaseUrl}/api/sessions/end`, {
|
|
12257
|
+
method: "POST",
|
|
12258
|
+
keepalive,
|
|
12259
|
+
headers: this.authHeaders(token),
|
|
12260
|
+
body: JSON.stringify({
|
|
12261
|
+
session_id: session.sessionId,
|
|
12262
|
+
end_reason: reason,
|
|
12263
|
+
final_total_ms: session.cumulativeTotalMs,
|
|
12264
|
+
final_active_ms: session.cumulativeActiveMs,
|
|
12265
|
+
final_passive_ms: session.cumulativePassiveMs,
|
|
12266
|
+
tracker_version: this.trackerVersion,
|
|
12267
|
+
activity_segments: session.pendingSegments.slice(0, this.maxSegmentsPerRequest)
|
|
12268
|
+
})
|
|
12269
|
+
});
|
|
12270
|
+
return response.ok;
|
|
12271
|
+
}
|
|
12272
|
+
authHeaders(token) {
|
|
12273
|
+
return {
|
|
12274
|
+
"Content-Type": "application/json",
|
|
12275
|
+
"Authorization": `Bearer ${token}`
|
|
12276
|
+
};
|
|
12277
|
+
}
|
|
12278
|
+
getDashboardView() {
|
|
12279
|
+
return typeof window !== "undefined" ? window.location.pathname : void 0;
|
|
12280
|
+
}
|
|
12281
|
+
};
|
|
12282
|
+
|
|
12006
12283
|
// src/lib/services/sessionTracker.ts
|
|
12007
12284
|
function generateSessionId() {
|
|
12008
12285
|
return `sess_${Date.now()}_${Math.random().toString(36).substring(2, 15)}`;
|
|
12009
12286
|
}
|
|
12287
|
+
function isDebugEnabled() {
|
|
12288
|
+
return typeof process !== "undefined" && process.env.NEXT_PUBLIC_DEBUG_DASHBOARD === "true";
|
|
12289
|
+
}
|
|
12290
|
+
function debugLog(message, data) {
|
|
12291
|
+
if (isDebugEnabled()) {
|
|
12292
|
+
console.log(`[SessionTracker] ${message}`, data || {});
|
|
12293
|
+
}
|
|
12294
|
+
}
|
|
12010
12295
|
var SessionTracker = class {
|
|
12011
12296
|
constructor(config) {
|
|
12012
12297
|
this.session = null;
|
|
12013
12298
|
this.heartbeatInterval = null;
|
|
12014
|
-
this.graceTimeout = null;
|
|
12015
12299
|
this.tickInterval = null;
|
|
12016
|
-
|
|
12300
|
+
this.heartbeatInFlight = false;
|
|
12301
|
+
this.pendingForcedHeartbeat = false;
|
|
12302
|
+
this.TRACKER_VERSION = 2;
|
|
12017
12303
|
this.HEARTBEAT_INTERVAL_MS = 3e4;
|
|
12018
|
-
// 30 seconds
|
|
12019
|
-
this.HIDDEN_GRACE_PERIOD_MS = 3e5;
|
|
12020
|
-
// 5 minutes
|
|
12021
12304
|
this.IDLE_THRESHOLD_MS = 3e4;
|
|
12022
|
-
// 30 seconds without activity = passive
|
|
12023
12305
|
this.TICK_INTERVAL_MS = 1e3;
|
|
12306
|
+
this.MAX_SEGMENTS_PER_REQUEST = 200;
|
|
12024
12307
|
this.config = config;
|
|
12308
|
+
this.activityAccumulator = new SessionActivityAccumulator(
|
|
12309
|
+
this.IDLE_THRESHOLD_MS,
|
|
12310
|
+
() => this.getDashboardView()
|
|
12311
|
+
);
|
|
12312
|
+
this.transport = new SessionTrackerTransport(
|
|
12313
|
+
this.config,
|
|
12314
|
+
this.TRACKER_VERSION,
|
|
12315
|
+
this.MAX_SEGMENTS_PER_REQUEST
|
|
12316
|
+
);
|
|
12025
12317
|
this.boundHandleActivity = this.handleUserActivity.bind(this);
|
|
12026
12318
|
this.boundHandleVisibility = this.handleVisibilityChange.bind(this);
|
|
12027
12319
|
this.boundHandleBeforeUnload = this.handleBeforeUnload.bind(this);
|
|
12320
|
+
this.boundHandlePageHide = this.handlePageHide.bind(this);
|
|
12321
|
+
this.boundHandlePageShow = this.handlePageShow.bind(this);
|
|
12028
12322
|
this.boundHandleFocus = this.handleWindowFocus.bind(this);
|
|
12029
12323
|
this.boundHandleBlur = this.handleWindowBlur.bind(this);
|
|
12030
12324
|
}
|
|
12031
|
-
/**
|
|
12032
|
-
* Start a new session
|
|
12033
|
-
*/
|
|
12034
12325
|
async startSession(userId, companyId) {
|
|
12035
12326
|
if (this.session && this.session.state !== "ENDED") {
|
|
12036
|
-
|
|
12327
|
+
debugLog("Session already active, ignoring start", { sessionId: this.session.sessionId });
|
|
12037
12328
|
return;
|
|
12038
12329
|
}
|
|
12039
12330
|
const sessionId = generateSessionId();
|
|
12040
12331
|
const now4 = /* @__PURE__ */ new Date();
|
|
12041
|
-
this.session =
|
|
12332
|
+
this.session = createSessionData(
|
|
12042
12333
|
sessionId,
|
|
12043
12334
|
userId,
|
|
12044
12335
|
companyId,
|
|
12045
|
-
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
12055
|
-
|
|
12056
|
-
|
|
12336
|
+
now4,
|
|
12337
|
+
this.getDocumentVisibility(),
|
|
12338
|
+
this.getWindowFocus()
|
|
12339
|
+
);
|
|
12340
|
+
await this.sendStartSession();
|
|
12341
|
+
if (!this.session || this.session.sessionId !== sessionId) {
|
|
12342
|
+
return;
|
|
12343
|
+
}
|
|
12344
|
+
if (this.session.state === "ENDED") {
|
|
12345
|
+
await this.sendEndSession(this.session.endReason || "navigation");
|
|
12346
|
+
debugLog("Session start completed after local end", { sessionId });
|
|
12347
|
+
return;
|
|
12348
|
+
}
|
|
12349
|
+
const trackingStart = /* @__PURE__ */ new Date();
|
|
12350
|
+
this.session.lastTick = trackingStart;
|
|
12351
|
+
this.session.isVisible = this.getDocumentVisibility();
|
|
12352
|
+
this.session.isFocused = this.getWindowFocus();
|
|
12057
12353
|
this.setupActivityListeners();
|
|
12058
12354
|
this.setupVisibilityListener();
|
|
12059
12355
|
this.setupFocusListeners();
|
|
12060
|
-
this.
|
|
12356
|
+
this.setupPageLifecycleListeners();
|
|
12061
12357
|
this.startTimeTick();
|
|
12062
12358
|
this.startHeartbeat();
|
|
12063
|
-
await this.sendStartSession();
|
|
12064
12359
|
this.config.onSessionStart?.(sessionId);
|
|
12065
|
-
|
|
12360
|
+
debugLog("Session started", { sessionId });
|
|
12066
12361
|
}
|
|
12067
|
-
/**
|
|
12068
|
-
* End the current session
|
|
12069
|
-
*/
|
|
12070
12362
|
async endSession(reason) {
|
|
12071
12363
|
if (!this.session || this.session.state === "ENDED") {
|
|
12072
12364
|
return;
|
|
12073
12365
|
}
|
|
12074
12366
|
const sessionId = this.session.sessionId;
|
|
12075
12367
|
this.updateTimes();
|
|
12368
|
+
this.flushCurrentSegment(/* @__PURE__ */ new Date());
|
|
12369
|
+
this.session.endReason = reason;
|
|
12370
|
+
await this.drainPendingSegmentsBeforeEnd();
|
|
12076
12371
|
this.session.state = "ENDED";
|
|
12077
12372
|
this.cleanup();
|
|
12078
12373
|
await this.sendEndSession(reason);
|
|
12079
12374
|
this.config.onSessionEnd?.(sessionId, reason);
|
|
12080
|
-
|
|
12375
|
+
debugLog("Session ended", { sessionId, reason });
|
|
12081
12376
|
}
|
|
12082
|
-
/**
|
|
12083
|
-
* Get current session stats (for debugging/display)
|
|
12084
|
-
*/
|
|
12085
12377
|
getSessionStats() {
|
|
12086
12378
|
if (!this.session) return null;
|
|
12087
12379
|
return {
|
|
@@ -12090,11 +12382,9 @@ var SessionTracker = class {
|
|
|
12090
12382
|
passiveMs: this.session.cumulativePassiveMs
|
|
12091
12383
|
};
|
|
12092
12384
|
}
|
|
12093
|
-
// ===== Private Methods =====
|
|
12094
12385
|
setupActivityListeners() {
|
|
12095
12386
|
if (typeof window === "undefined") return;
|
|
12096
|
-
|
|
12097
|
-
events.forEach((event) => {
|
|
12387
|
+
TRACKED_ACTIVITY_EVENTS.forEach((event) => {
|
|
12098
12388
|
window.addEventListener(event, this.boundHandleActivity, { passive: true });
|
|
12099
12389
|
});
|
|
12100
12390
|
}
|
|
@@ -12107,71 +12397,100 @@ var SessionTracker = class {
|
|
|
12107
12397
|
window.addEventListener("focus", this.boundHandleFocus);
|
|
12108
12398
|
window.addEventListener("blur", this.boundHandleBlur);
|
|
12109
12399
|
}
|
|
12110
|
-
|
|
12400
|
+
setupPageLifecycleListeners() {
|
|
12111
12401
|
if (typeof window === "undefined") return;
|
|
12112
12402
|
window.addEventListener("beforeunload", this.boundHandleBeforeUnload);
|
|
12403
|
+
window.addEventListener("pagehide", this.boundHandlePageHide);
|
|
12404
|
+
window.addEventListener("pageshow", this.boundHandlePageShow);
|
|
12113
12405
|
}
|
|
12114
|
-
handleUserActivity() {
|
|
12406
|
+
handleUserActivity(event) {
|
|
12115
12407
|
if (!this.session || this.session.state === "ENDED") return;
|
|
12408
|
+
if ("isTrusted" in event && event.isTrusted === false) return;
|
|
12409
|
+
this.updateTimes();
|
|
12116
12410
|
this.session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
12117
12411
|
this.session.isUserActive = true;
|
|
12118
12412
|
}
|
|
12119
12413
|
handleVisibilityChange() {
|
|
12120
12414
|
if (!this.session || this.session.state === "ENDED") return;
|
|
12415
|
+
const now4 = /* @__PURE__ */ new Date();
|
|
12416
|
+
this.updateTimes(now4);
|
|
12121
12417
|
if (document.visibilityState === "hidden") {
|
|
12122
|
-
this.
|
|
12123
|
-
this.session.state = "BACKGROUND_GRACE";
|
|
12418
|
+
this.flushCurrentSegment(now4);
|
|
12124
12419
|
this.session.isVisible = false;
|
|
12125
12420
|
this.session.isFocused = false;
|
|
12126
|
-
this.session.
|
|
12127
|
-
this.
|
|
12128
|
-
|
|
12129
|
-
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
if (this.graceTimeout) {
|
|
12133
|
-
clearTimeout(this.graceTimeout);
|
|
12134
|
-
this.graceTimeout = null;
|
|
12135
|
-
}
|
|
12136
|
-
this.session.hiddenSince = null;
|
|
12137
|
-
this.session.state = "ACTIVE";
|
|
12138
|
-
this.session.isVisible = true;
|
|
12139
|
-
this.session.isFocused = typeof document !== "undefined" && typeof document.hasFocus === "function" ? document.hasFocus() : true;
|
|
12140
|
-
this.session.lastActivityAt = /* @__PURE__ */ new Date();
|
|
12141
|
-
this.session.isUserActive = true;
|
|
12142
|
-
this.session.lastTick = /* @__PURE__ */ new Date();
|
|
12143
|
-
console.log("[SessionTracker] Tab visible again");
|
|
12421
|
+
this.session.isUserActive = false;
|
|
12422
|
+
this.session.lastActivityAt = null;
|
|
12423
|
+
this.session.lastTick = now4;
|
|
12424
|
+
void this.sendHeartbeat(true);
|
|
12425
|
+
debugLog("Tab hidden");
|
|
12426
|
+
return;
|
|
12144
12427
|
}
|
|
12428
|
+
this.session.isVisible = true;
|
|
12429
|
+
this.session.isFocused = this.getWindowFocus();
|
|
12430
|
+
this.session.isUserActive = false;
|
|
12431
|
+
this.session.lastActivityAt = null;
|
|
12432
|
+
this.session.lastTick = now4;
|
|
12433
|
+
void this.sendHeartbeat(true);
|
|
12434
|
+
debugLog("Tab visible");
|
|
12145
12435
|
}
|
|
12146
12436
|
handleWindowFocus() {
|
|
12147
12437
|
if (!this.session || this.session.state === "ENDED") return;
|
|
12438
|
+
const now4 = /* @__PURE__ */ new Date();
|
|
12439
|
+
this.updateTimes(now4);
|
|
12148
12440
|
this.session.isFocused = true;
|
|
12149
|
-
this.session.
|
|
12150
|
-
this.session.
|
|
12151
|
-
this.session.lastTick =
|
|
12441
|
+
this.session.isUserActive = false;
|
|
12442
|
+
this.session.lastActivityAt = null;
|
|
12443
|
+
this.session.lastTick = now4;
|
|
12444
|
+
void this.sendHeartbeat(true);
|
|
12152
12445
|
}
|
|
12153
12446
|
handleWindowBlur() {
|
|
12154
12447
|
if (!this.session || this.session.state === "ENDED") return;
|
|
12448
|
+
const now4 = /* @__PURE__ */ new Date();
|
|
12449
|
+
this.updateTimes(now4);
|
|
12450
|
+
this.flushCurrentSegment(now4);
|
|
12155
12451
|
this.session.isFocused = false;
|
|
12156
|
-
this.session.
|
|
12452
|
+
this.session.isUserActive = false;
|
|
12453
|
+
this.session.lastActivityAt = null;
|
|
12454
|
+
this.session.lastTick = now4;
|
|
12455
|
+
void this.sendHeartbeat(true);
|
|
12157
12456
|
}
|
|
12158
|
-
handleBeforeUnload(
|
|
12159
|
-
|
|
12160
|
-
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
this.
|
|
12173
|
-
|
|
12457
|
+
handleBeforeUnload() {
|
|
12458
|
+
this.finalizeWithKeepalive("tab_close");
|
|
12459
|
+
}
|
|
12460
|
+
handlePageHide(event) {
|
|
12461
|
+
if (event.persisted) {
|
|
12462
|
+
if (!this.session || this.session.state === "ENDED") return;
|
|
12463
|
+
const now4 = /* @__PURE__ */ new Date();
|
|
12464
|
+
this.updateTimes(now4);
|
|
12465
|
+
this.flushCurrentSegment(now4);
|
|
12466
|
+
this.session.isVisible = false;
|
|
12467
|
+
this.session.isFocused = false;
|
|
12468
|
+
this.session.isUserActive = false;
|
|
12469
|
+
this.session.lastActivityAt = null;
|
|
12470
|
+
this.session.lastTick = now4;
|
|
12471
|
+
void this.sendHeartbeat(true);
|
|
12472
|
+
return;
|
|
12174
12473
|
}
|
|
12474
|
+
this.finalizeWithKeepalive("pagehide");
|
|
12475
|
+
}
|
|
12476
|
+
handlePageShow(event) {
|
|
12477
|
+
if (!event.persisted || !this.session || this.session.state === "ENDED") return;
|
|
12478
|
+
const now4 = /* @__PURE__ */ new Date();
|
|
12479
|
+
this.session.isVisible = this.getDocumentVisibility();
|
|
12480
|
+
this.session.isFocused = this.getWindowFocus();
|
|
12481
|
+
this.session.isUserActive = false;
|
|
12482
|
+
this.session.lastActivityAt = null;
|
|
12483
|
+
this.session.lastTick = now4;
|
|
12484
|
+
void this.sendHeartbeat(true);
|
|
12485
|
+
}
|
|
12486
|
+
finalizeWithKeepalive(reason) {
|
|
12487
|
+
if (!this.session || this.session.state === "ENDED") return;
|
|
12488
|
+
this.updateTimes();
|
|
12489
|
+
this.flushCurrentSegment(/* @__PURE__ */ new Date());
|
|
12490
|
+
this.session.endReason = reason;
|
|
12491
|
+
this.session.state = "ENDED";
|
|
12492
|
+
void this.sendEndSession(reason, true);
|
|
12493
|
+
this.cleanup();
|
|
12175
12494
|
}
|
|
12176
12495
|
startTimeTick() {
|
|
12177
12496
|
this.tickInterval = setInterval(() => {
|
|
@@ -12179,30 +12498,17 @@ var SessionTracker = class {
|
|
|
12179
12498
|
}, this.TICK_INTERVAL_MS);
|
|
12180
12499
|
}
|
|
12181
12500
|
shouldTrackTime() {
|
|
12182
|
-
|
|
12183
|
-
const stateAllowsTracking = this.session.state === "ACTIVE" || this.session.state === "BACKGROUND_GRACE";
|
|
12184
|
-
return stateAllowsTracking && this.session.isVisible && this.session.isFocused;
|
|
12501
|
+
return this.activityAccumulator.shouldTrackTime(this.session);
|
|
12185
12502
|
}
|
|
12186
|
-
|
|
12187
|
-
if (!this.session
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
this.session.cumulativeTotalMs += elapsed;
|
|
12196
|
-
const timeSinceActivity = now4.getTime() - this.session.lastActivityAt.getTime();
|
|
12197
|
-
if (timeSinceActivity < this.IDLE_THRESHOLD_MS) {
|
|
12198
|
-
this.session.cumulativeActiveMs += elapsed;
|
|
12199
|
-
this.session.isUserActive = true;
|
|
12200
|
-
} else {
|
|
12201
|
-
this.session.cumulativePassiveMs += elapsed;
|
|
12202
|
-
this.session.isUserActive = false;
|
|
12203
|
-
}
|
|
12204
|
-
}
|
|
12205
|
-
this.session.lastTick = now4;
|
|
12503
|
+
getActivityState(now4) {
|
|
12504
|
+
if (!this.session) return "passive";
|
|
12505
|
+
return this.activityAccumulator.getActivityState(this.session, now4);
|
|
12506
|
+
}
|
|
12507
|
+
updateTimes(now4 = /* @__PURE__ */ new Date()) {
|
|
12508
|
+
this.activityAccumulator.updateTimes(this.session, now4);
|
|
12509
|
+
}
|
|
12510
|
+
flushCurrentSegment(endAt) {
|
|
12511
|
+
this.activityAccumulator.flushCurrentSegment(this.session, endAt);
|
|
12206
12512
|
}
|
|
12207
12513
|
startHeartbeat() {
|
|
12208
12514
|
this.heartbeatInterval = setInterval(async () => {
|
|
@@ -12212,75 +12518,70 @@ var SessionTracker = class {
|
|
|
12212
12518
|
async sendStartSession() {
|
|
12213
12519
|
if (!this.session) return;
|
|
12214
12520
|
try {
|
|
12215
|
-
|
|
12216
|
-
if (!token) {
|
|
12217
|
-
console.warn("[SessionTracker] No access token, skipping start session API call");
|
|
12218
|
-
return;
|
|
12219
|
-
}
|
|
12220
|
-
await fetch(`${this.config.apiBaseUrl}/api/sessions/start`, {
|
|
12221
|
-
method: "POST",
|
|
12222
|
-
headers: {
|
|
12223
|
-
"Content-Type": "application/json",
|
|
12224
|
-
"Authorization": `Bearer ${token}`
|
|
12225
|
-
},
|
|
12226
|
-
body: JSON.stringify({
|
|
12227
|
-
session_id: this.session.sessionId,
|
|
12228
|
-
user_agent: typeof navigator !== "undefined" ? navigator.userAgent : void 0,
|
|
12229
|
-
dashboard_view: typeof window !== "undefined" ? window.location.pathname : void 0
|
|
12230
|
-
})
|
|
12231
|
-
});
|
|
12521
|
+
await this.transport.sendStartSession(this.session);
|
|
12232
12522
|
} catch (error) {
|
|
12233
12523
|
console.error("[SessionTracker] Failed to send start session:", error);
|
|
12234
12524
|
}
|
|
12235
12525
|
}
|
|
12236
|
-
async sendHeartbeat() {
|
|
12526
|
+
async sendHeartbeat(force = false, allowReplacement = true) {
|
|
12527
|
+
if (this.heartbeatInFlight) {
|
|
12528
|
+
this.pendingForcedHeartbeat || (this.pendingForcedHeartbeat = force);
|
|
12529
|
+
return;
|
|
12530
|
+
}
|
|
12237
12531
|
if (!this.session || this.session.state === "ENDED") return;
|
|
12532
|
+
this.heartbeatInFlight = true;
|
|
12238
12533
|
try {
|
|
12239
|
-
|
|
12240
|
-
|
|
12241
|
-
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12246
|
-
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
|
|
12250
|
-
|
|
12251
|
-
|
|
12252
|
-
|
|
12253
|
-
|
|
12254
|
-
|
|
12255
|
-
|
|
12256
|
-
});
|
|
12534
|
+
this.updateTimes();
|
|
12535
|
+
this.flushCurrentSegment(/* @__PURE__ */ new Date());
|
|
12536
|
+
if (!force && !this.shouldTrackTime()) return;
|
|
12537
|
+
const result = await this.transport.sendHeartbeat(this.session);
|
|
12538
|
+
if (!result) return;
|
|
12539
|
+
if (result.ok && result.acknowledged !== false) {
|
|
12540
|
+
debugLog("Heartbeat acknowledged", { segments: result.sentSegmentIds.size });
|
|
12541
|
+
this.session.pendingSegments = this.session.pendingSegments.filter(
|
|
12542
|
+
(segment) => !result.sentSegmentIds.has(segment.client_segment_id)
|
|
12543
|
+
);
|
|
12544
|
+
} else if (result.ok && result.acknowledged === false && allowReplacement && this.shouldTrackTime()) {
|
|
12545
|
+
const { userId, companyId, sessionId } = this.session;
|
|
12546
|
+
this.session.state = "ENDED";
|
|
12547
|
+
this.cleanup();
|
|
12548
|
+
debugLog("Heartbeat not acknowledged, starting replacement session", { sessionId });
|
|
12549
|
+
await this.startSession(userId, companyId);
|
|
12550
|
+
}
|
|
12257
12551
|
} catch (error) {
|
|
12258
12552
|
console.error("[SessionTracker] Failed to send heartbeat:", error);
|
|
12553
|
+
} finally {
|
|
12554
|
+
this.heartbeatInFlight = false;
|
|
12555
|
+
if (this.pendingForcedHeartbeat && this.session?.state !== "ENDED") {
|
|
12556
|
+
this.pendingForcedHeartbeat = false;
|
|
12557
|
+
void this.sendHeartbeat(true);
|
|
12558
|
+
}
|
|
12259
12559
|
}
|
|
12260
12560
|
}
|
|
12261
|
-
async
|
|
12561
|
+
async drainPendingSegmentsBeforeEnd() {
|
|
12562
|
+
if (!this.session) return;
|
|
12563
|
+
let guard = 0;
|
|
12564
|
+
while (this.session.state !== "ENDED" && this.session.pendingSegments.length > this.MAX_SEGMENTS_PER_REQUEST && guard < 20) {
|
|
12565
|
+
const pendingBefore = this.session.pendingSegments.length;
|
|
12566
|
+
await this.sendHeartbeat(true, false);
|
|
12567
|
+
if (!this.session || this.session.pendingSegments.length >= pendingBefore) {
|
|
12568
|
+
break;
|
|
12569
|
+
}
|
|
12570
|
+
guard += 1;
|
|
12571
|
+
}
|
|
12572
|
+
}
|
|
12573
|
+
async sendEndSession(reason, keepalive = false) {
|
|
12262
12574
|
if (!this.session) return;
|
|
12263
12575
|
try {
|
|
12264
|
-
const
|
|
12265
|
-
if (
|
|
12266
|
-
|
|
12267
|
-
return;
|
|
12576
|
+
const ok = await this.transport.sendEndSession(this.session, reason, keepalive);
|
|
12577
|
+
if (ok) {
|
|
12578
|
+
this.session.pendingSegments = this.session.pendingSegments.slice(this.MAX_SEGMENTS_PER_REQUEST);
|
|
12268
12579
|
}
|
|
12269
|
-
await fetch(`${this.config.apiBaseUrl}/api/sessions/end`, {
|
|
12270
|
-
method: "POST",
|
|
12271
|
-
headers: {
|
|
12272
|
-
"Content-Type": "application/json",
|
|
12273
|
-
"Authorization": `Bearer ${token}`
|
|
12274
|
-
},
|
|
12275
|
-
body: JSON.stringify({
|
|
12276
|
-
session_id: this.session.sessionId,
|
|
12277
|
-
end_reason: reason,
|
|
12278
|
-
final_total_ms: this.session.cumulativeTotalMs,
|
|
12279
|
-
final_active_ms: this.session.cumulativeActiveMs,
|
|
12280
|
-
final_passive_ms: this.session.cumulativePassiveMs
|
|
12281
|
-
})
|
|
12282
|
-
});
|
|
12283
12580
|
} catch (error) {
|
|
12581
|
+
if (keepalive) {
|
|
12582
|
+
debugLog("Keepalive end session failed after page lifecycle transition", { reason });
|
|
12583
|
+
return;
|
|
12584
|
+
}
|
|
12284
12585
|
console.error("[SessionTracker] Failed to send end session:", error);
|
|
12285
12586
|
}
|
|
12286
12587
|
}
|
|
@@ -12293,23 +12594,29 @@ var SessionTracker = class {
|
|
|
12293
12594
|
clearInterval(this.tickInterval);
|
|
12294
12595
|
this.tickInterval = null;
|
|
12295
12596
|
}
|
|
12296
|
-
if (this.graceTimeout) {
|
|
12297
|
-
clearTimeout(this.graceTimeout);
|
|
12298
|
-
this.graceTimeout = null;
|
|
12299
|
-
}
|
|
12300
12597
|
if (typeof window !== "undefined") {
|
|
12301
|
-
|
|
12302
|
-
events.forEach((event) => {
|
|
12598
|
+
TRACKED_ACTIVITY_EVENTS.forEach((event) => {
|
|
12303
12599
|
window.removeEventListener(event, this.boundHandleActivity);
|
|
12304
12600
|
});
|
|
12305
12601
|
window.removeEventListener("focus", this.boundHandleFocus);
|
|
12306
12602
|
window.removeEventListener("blur", this.boundHandleBlur);
|
|
12307
12603
|
window.removeEventListener("beforeunload", this.boundHandleBeforeUnload);
|
|
12604
|
+
window.removeEventListener("pagehide", this.boundHandlePageHide);
|
|
12605
|
+
window.removeEventListener("pageshow", this.boundHandlePageShow);
|
|
12308
12606
|
}
|
|
12309
12607
|
if (typeof document !== "undefined") {
|
|
12310
12608
|
document.removeEventListener("visibilitychange", this.boundHandleVisibility);
|
|
12311
12609
|
}
|
|
12312
12610
|
}
|
|
12611
|
+
getDashboardView() {
|
|
12612
|
+
return typeof window !== "undefined" ? window.location.pathname : void 0;
|
|
12613
|
+
}
|
|
12614
|
+
getDocumentVisibility() {
|
|
12615
|
+
return typeof document !== "undefined" ? document.visibilityState === "visible" : true;
|
|
12616
|
+
}
|
|
12617
|
+
getWindowFocus() {
|
|
12618
|
+
return typeof document !== "undefined" && typeof document.hasFocus === "function" ? document.hasFocus() : true;
|
|
12619
|
+
}
|
|
12313
12620
|
};
|
|
12314
12621
|
function createSessionTracker(config) {
|
|
12315
12622
|
return new SessionTracker(config);
|
|
@@ -13315,15 +13622,43 @@ function useSessionTracking(options = {}) {
|
|
|
13315
13622
|
const trackerRef = React125.useRef(null);
|
|
13316
13623
|
const isTrackingRef = React125.useRef(false);
|
|
13317
13624
|
const initRef = React125.useRef(false);
|
|
13318
|
-
const
|
|
13319
|
-
|
|
13625
|
+
const accessTokenRef = React125.useRef(null);
|
|
13626
|
+
const isAuthenticatedRef = React125.useRef(isAuthenticated);
|
|
13627
|
+
const onSessionStartRef = React125.useRef(onSessionStart);
|
|
13628
|
+
const onSessionEndRef = React125.useRef(onSessionEnd);
|
|
13629
|
+
const apiBaseUrl2 = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
13630
|
+
isAuthenticatedRef.current = isAuthenticated;
|
|
13631
|
+
React125.useEffect(() => {
|
|
13632
|
+
if (session?.access_token) {
|
|
13633
|
+
accessTokenRef.current = session.access_token;
|
|
13634
|
+
}
|
|
13320
13635
|
}, [session?.access_token]);
|
|
13636
|
+
React125.useEffect(() => {
|
|
13637
|
+
onSessionStartRef.current = onSessionStart;
|
|
13638
|
+
}, [onSessionStart]);
|
|
13639
|
+
React125.useEffect(() => {
|
|
13640
|
+
onSessionEndRef.current = onSessionEnd;
|
|
13641
|
+
}, [onSessionEnd]);
|
|
13642
|
+
const getAccessToken2 = React125.useCallback(async () => {
|
|
13643
|
+
return accessTokenRef.current;
|
|
13644
|
+
}, []);
|
|
13645
|
+
const endCurrentTracker = React125.useCallback((reason) => {
|
|
13646
|
+
const tracker = trackerRef.current;
|
|
13647
|
+
if (!tracker) return null;
|
|
13648
|
+
const tokenForEndingSession = accessTokenRef.current;
|
|
13649
|
+
trackerRef.current = null;
|
|
13650
|
+
isTrackingRef.current = false;
|
|
13651
|
+
return tracker.endSession(reason).finally(() => {
|
|
13652
|
+
if (reason === "logout" && !isAuthenticatedRef.current && accessTokenRef.current === tokenForEndingSession) {
|
|
13653
|
+
accessTokenRef.current = null;
|
|
13654
|
+
}
|
|
13655
|
+
});
|
|
13656
|
+
}, []);
|
|
13321
13657
|
React125.useEffect(() => {
|
|
13322
13658
|
if (!enabled || !isAuthenticated || !user || !session || initRef.current) {
|
|
13323
13659
|
return;
|
|
13324
13660
|
}
|
|
13325
13661
|
initRef.current = true;
|
|
13326
|
-
const apiBaseUrl2 = config?.apiBaseUrl || process.env.NEXT_PUBLIC_API_BASE_URL || "";
|
|
13327
13662
|
if (!apiBaseUrl2) {
|
|
13328
13663
|
console.warn("[useSessionTracking] No API base URL configured, session tracking disabled");
|
|
13329
13664
|
return;
|
|
@@ -13331,41 +13666,34 @@ function useSessionTracking(options = {}) {
|
|
|
13331
13666
|
const tracker = createSessionTracker({
|
|
13332
13667
|
apiBaseUrl: apiBaseUrl2,
|
|
13333
13668
|
getAccessToken: getAccessToken2,
|
|
13669
|
+
getCachedAccessToken: () => accessTokenRef.current,
|
|
13334
13670
|
onSessionStart: (sessionId) => {
|
|
13335
13671
|
isTrackingRef.current = true;
|
|
13336
|
-
|
|
13672
|
+
onSessionStartRef.current?.(sessionId);
|
|
13337
13673
|
},
|
|
13338
13674
|
onSessionEnd: (sessionId, reason) => {
|
|
13339
13675
|
isTrackingRef.current = false;
|
|
13340
|
-
|
|
13676
|
+
onSessionEndRef.current?.(sessionId, reason);
|
|
13341
13677
|
}
|
|
13342
13678
|
});
|
|
13343
13679
|
trackerRef.current = tracker;
|
|
13344
13680
|
const companyId = user?.company_id || user?.properties?.company_id || null;
|
|
13345
13681
|
tracker.startSession(user.id, companyId);
|
|
13346
13682
|
return () => {
|
|
13347
|
-
|
|
13348
|
-
|
|
13349
|
-
trackerRef.current = null;
|
|
13350
|
-
}
|
|
13683
|
+
const reason = isAuthenticatedRef.current ? "navigation" : "logout";
|
|
13684
|
+
void endCurrentTracker(reason);
|
|
13351
13685
|
initRef.current = false;
|
|
13352
13686
|
isTrackingRef.current = false;
|
|
13353
13687
|
};
|
|
13354
|
-
}, [enabled, isAuthenticated, user?.id,
|
|
13688
|
+
}, [enabled, isAuthenticated, user?.id, apiBaseUrl2, getAccessToken2, endCurrentTracker]);
|
|
13355
13689
|
React125.useEffect(() => {
|
|
13356
13690
|
if (!isAuthenticated && trackerRef.current && isTrackingRef.current) {
|
|
13357
|
-
|
|
13358
|
-
trackerRef.current = null;
|
|
13359
|
-
isTrackingRef.current = false;
|
|
13691
|
+
void endCurrentTracker("logout");
|
|
13360
13692
|
}
|
|
13361
|
-
}, [isAuthenticated]);
|
|
13693
|
+
}, [isAuthenticated, endCurrentTracker]);
|
|
13362
13694
|
const endSession = React125.useCallback(async (reason) => {
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
trackerRef.current = null;
|
|
13366
|
-
isTrackingRef.current = false;
|
|
13367
|
-
}
|
|
13368
|
-
}, []);
|
|
13695
|
+
await endCurrentTracker(reason);
|
|
13696
|
+
}, [endCurrentTracker]);
|
|
13369
13697
|
const sessionStats = trackerRef.current?.getSessionStats() || null;
|
|
13370
13698
|
return {
|
|
13371
13699
|
sessionStats,
|
|
@@ -17464,7 +17792,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
17464
17792
|
return Number.isFinite(parsed) ? parsed : DEFAULT_MAX_MANIFEST_AGE_MS;
|
|
17465
17793
|
})();
|
|
17466
17794
|
const manifestStaleThresholdMs = maxManifestAgeMs > 0 ? Math.min(maxManifestAgeMs, SEGMENT_MAX_AGE_MS) : SEGMENT_MAX_AGE_MS;
|
|
17467
|
-
const
|
|
17795
|
+
const debugLog2 = (...args) => {
|
|
17468
17796
|
if (debugEnabled) {
|
|
17469
17797
|
console.log(...args);
|
|
17470
17798
|
}
|
|
@@ -17646,7 +17974,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
17646
17974
|
return;
|
|
17647
17975
|
}
|
|
17648
17976
|
} catch (error) {
|
|
17649
|
-
|
|
17977
|
+
debugLog2("[HLS] Stale manifest poll failed", error);
|
|
17650
17978
|
}
|
|
17651
17979
|
staleManifestPollDelayRef.current = Math.min(
|
|
17652
17980
|
staleManifestPollDelayRef.current + 5e3,
|
|
@@ -17996,7 +18324,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
17996
18324
|
}
|
|
17997
18325
|
return true;
|
|
17998
18326
|
} catch (error) {
|
|
17999
|
-
|
|
18327
|
+
debugLog2("[HLS] Manifest freshness check failed", error);
|
|
18000
18328
|
{
|
|
18001
18329
|
markStaleStream("manifest freshness check failed");
|
|
18002
18330
|
return false;
|
|
@@ -18042,7 +18370,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
18042
18370
|
};
|
|
18043
18371
|
const softRestart = (reason) => {
|
|
18044
18372
|
if (staleManifestTriggeredRef.current) {
|
|
18045
|
-
|
|
18373
|
+
debugLog2("[HLS] Skip soft restart while manifest is stale", reason);
|
|
18046
18374
|
return;
|
|
18047
18375
|
}
|
|
18048
18376
|
console.warn(`[HLS] Soft restart: ${reason}`);
|
|
@@ -18075,7 +18403,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
18075
18403
|
};
|
|
18076
18404
|
const hardRestart = (reason) => {
|
|
18077
18405
|
if (staleManifestTriggeredRef.current) {
|
|
18078
|
-
|
|
18406
|
+
debugLog2("[HLS] Skip hard restart while manifest is stale", reason);
|
|
18079
18407
|
return;
|
|
18080
18408
|
}
|
|
18081
18409
|
console.warn(`[HLS] Hard restart: ${reason}`);
|
|
@@ -18329,7 +18657,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
18329
18657
|
hls.loadSource(resolvedHlsSrc);
|
|
18330
18658
|
activeStreamUrlRef.current = resolvedHlsSrc;
|
|
18331
18659
|
hls.on(Hls__default.default.Events.ERROR, (_, data) => {
|
|
18332
|
-
|
|
18660
|
+
debugLog2("[HLS] Error", {
|
|
18333
18661
|
type: data.type,
|
|
18334
18662
|
details: data.details,
|
|
18335
18663
|
fatal: data.fatal,
|
|
@@ -18337,7 +18665,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
18337
18665
|
frag: data.frag?.sn
|
|
18338
18666
|
});
|
|
18339
18667
|
if (data.type === Hls__default.default.ErrorTypes.MEDIA_ERROR && data.details === Hls__default.default.ErrorDetails.BUFFER_STALLED_ERROR) {
|
|
18340
|
-
|
|
18668
|
+
debugLog2("[HLS] Buffer stalled, waiting for next segment");
|
|
18341
18669
|
attemptPlay();
|
|
18342
18670
|
return;
|
|
18343
18671
|
}
|
|
@@ -18481,7 +18809,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError, hlsConfig }) {
|
|
|
18481
18809
|
}
|
|
18482
18810
|
}
|
|
18483
18811
|
}
|
|
18484
|
-
|
|
18812
|
+
debugLog2("[HLS] Level loaded", {
|
|
18485
18813
|
targetduration: details.targetduration,
|
|
18486
18814
|
edge: details.edge,
|
|
18487
18815
|
fragments: data.details?.fragments?.length
|
|
@@ -18877,6 +19205,7 @@ var isInitializing = false;
|
|
|
18877
19205
|
var initializedWithLineIds = [];
|
|
18878
19206
|
var missingLineContextWarnings = /* @__PURE__ */ new Set();
|
|
18879
19207
|
var lineLoadPromises = /* @__PURE__ */ new Map();
|
|
19208
|
+
var GLOBAL_CACHE_KEY = "global";
|
|
18880
19209
|
var initializationPromise = null;
|
|
18881
19210
|
var workspaceDisplayNamesListeners = /* @__PURE__ */ new Set();
|
|
18882
19211
|
var notifyWorkspaceDisplayNamesListeners = (changedLineId) => {
|
|
@@ -18898,6 +19227,22 @@ var storeLineDisplayNames = (lineId, lineDisplayNamesMap) => {
|
|
|
18898
19227
|
runtimeWorkspaceDisplayNames[lineId][workspaceId] = displayName;
|
|
18899
19228
|
});
|
|
18900
19229
|
};
|
|
19230
|
+
var storeGlobalDisplayNames = (displayNamesMap) => {
|
|
19231
|
+
runtimeWorkspaceDisplayNames[GLOBAL_CACHE_KEY] = {};
|
|
19232
|
+
displayNamesMap.forEach((displayName, workspaceId) => {
|
|
19233
|
+
runtimeWorkspaceDisplayNames[GLOBAL_CACHE_KEY][workspaceId] = displayName;
|
|
19234
|
+
});
|
|
19235
|
+
};
|
|
19236
|
+
var storeDisplayNamesByLine = (displayNamesByLine, requestedLineIds = []) => {
|
|
19237
|
+
displayNamesByLine.forEach((lineDisplayNamesMap, lineId) => {
|
|
19238
|
+
storeLineDisplayNames(lineId, lineDisplayNamesMap);
|
|
19239
|
+
});
|
|
19240
|
+
requestedLineIds.forEach((lineId) => {
|
|
19241
|
+
if (!runtimeWorkspaceDisplayNames[lineId]) {
|
|
19242
|
+
runtimeWorkspaceDisplayNames[lineId] = {};
|
|
19243
|
+
}
|
|
19244
|
+
});
|
|
19245
|
+
};
|
|
18901
19246
|
var ensureLineWorkspaceDisplayNamesLoaded = async (lineId) => {
|
|
18902
19247
|
if (!lineId || runtimeWorkspaceDisplayNames[lineId]) {
|
|
18903
19248
|
return;
|
|
@@ -18981,7 +19326,11 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
|
|
|
18981
19326
|
console.log("\u{1F504} Target line IDs for workspace filtering:", targetLineIds);
|
|
18982
19327
|
runtimeWorkspaceDisplayNames = {};
|
|
18983
19328
|
lineLoadPromises.clear();
|
|
18984
|
-
if (targetLineIds.length >
|
|
19329
|
+
if (targetLineIds.length > 1) {
|
|
19330
|
+
const displayNamesByLine = await workspaceService.getWorkspaceDisplayNamesByLine(void 0, targetLineIds);
|
|
19331
|
+
storeDisplayNamesByLine(displayNamesByLine, targetLineIds);
|
|
19332
|
+
console.log(`\u2705 Stored display names for ${displayNamesByLine.size} authorized lines`);
|
|
19333
|
+
} else if (targetLineIds.length === 1) {
|
|
18985
19334
|
const results = await Promise.all(
|
|
18986
19335
|
targetLineIds.map(async (lineId) => {
|
|
18987
19336
|
console.log(`\u{1F504} Fetching workspaces for line: ${lineId}`);
|
|
@@ -18996,10 +19345,7 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
|
|
|
18996
19345
|
} else {
|
|
18997
19346
|
console.warn("\u26A0\uFE0F No line IDs found, fetching all workspaces (less efficient)");
|
|
18998
19347
|
const allWorkspacesMap = await workspaceService.getWorkspaceDisplayNames();
|
|
18999
|
-
|
|
19000
|
-
allWorkspacesMap.forEach((displayName, workspaceId) => {
|
|
19001
|
-
runtimeWorkspaceDisplayNames["global"][workspaceId] = displayName;
|
|
19002
|
-
});
|
|
19348
|
+
storeGlobalDisplayNames(allWorkspacesMap);
|
|
19003
19349
|
}
|
|
19004
19350
|
isInitialized = true;
|
|
19005
19351
|
initializedWithLineIds = targetLineIds;
|
|
@@ -19015,6 +19361,37 @@ async function initializeWorkspaceDisplayNames(explicitLineId) {
|
|
|
19015
19361
|
})();
|
|
19016
19362
|
await initializationPromise;
|
|
19017
19363
|
}
|
|
19364
|
+
var preInitializeWorkspaceDisplayNamesForLines = async (lineIds) => {
|
|
19365
|
+
const uniqueLineIds = Array.from(new Set((lineIds || []).filter(Boolean)));
|
|
19366
|
+
if (uniqueLineIds.length === 0) {
|
|
19367
|
+
await preInitializeWorkspaceDisplayNames();
|
|
19368
|
+
return;
|
|
19369
|
+
}
|
|
19370
|
+
if (uniqueLineIds.length === 1) {
|
|
19371
|
+
await preInitializeWorkspaceDisplayNames(uniqueLineIds[0]);
|
|
19372
|
+
return;
|
|
19373
|
+
}
|
|
19374
|
+
if (isInitialized && uniqueLineIds.every((lineId) => Boolean(runtimeWorkspaceDisplayNames[lineId]))) {
|
|
19375
|
+
return;
|
|
19376
|
+
}
|
|
19377
|
+
if (initializationPromise) {
|
|
19378
|
+
await initializationPromise;
|
|
19379
|
+
if (uniqueLineIds.every((lineId) => Boolean(runtimeWorkspaceDisplayNames[lineId]))) {
|
|
19380
|
+
return;
|
|
19381
|
+
}
|
|
19382
|
+
}
|
|
19383
|
+
isInitializing = true;
|
|
19384
|
+
initializationPromise = workspaceService.getWorkspaceDisplayNamesByLine(void 0, uniqueLineIds).then((displayNamesByLine) => {
|
|
19385
|
+
storeDisplayNamesByLine(displayNamesByLine, uniqueLineIds);
|
|
19386
|
+
initializedWithLineIds = uniqueLineIds;
|
|
19387
|
+
isInitialized = true;
|
|
19388
|
+
notifyWorkspaceDisplayNamesListeners();
|
|
19389
|
+
}).finally(() => {
|
|
19390
|
+
isInitializing = false;
|
|
19391
|
+
initializationPromise = null;
|
|
19392
|
+
});
|
|
19393
|
+
await initializationPromise;
|
|
19394
|
+
};
|
|
19018
19395
|
var preInitializeWorkspaceDisplayNames = async (lineId) => {
|
|
19019
19396
|
console.log("\u{1F504} preInitializeWorkspaceDisplayNames called for lineId:", lineId);
|
|
19020
19397
|
if (isInitialized) {
|
|
@@ -38749,6 +39126,7 @@ var HourlyOutputChartComponent = ({
|
|
|
38749
39126
|
timeRange: data2.timeRange,
|
|
38750
39127
|
startTime,
|
|
38751
39128
|
endTime,
|
|
39129
|
+
timezone,
|
|
38752
39130
|
output: Math.round(data2.originalOutput || 0),
|
|
38753
39131
|
target: data2.target,
|
|
38754
39132
|
status: data2.status
|
|
@@ -38922,6 +39300,7 @@ var HourlyOutputChartComponent = ({
|
|
|
38922
39300
|
timeRange: entry.timeRange,
|
|
38923
39301
|
startTime,
|
|
38924
39302
|
endTime,
|
|
39303
|
+
timezone,
|
|
38925
39304
|
output: Math.round(entry.originalOutput || 0),
|
|
38926
39305
|
target: entry.target,
|
|
38927
39306
|
status: entry.status
|
|
@@ -42389,6 +42768,65 @@ var buildPrefetchedExplorerMetadata = (activeFilter, metadataCategoryId, categor
|
|
|
42389
42768
|
};
|
|
42390
42769
|
};
|
|
42391
42770
|
var shouldDeferClipPlayerRender = (cropLoading, workspaceCrop) => cropLoading && workspaceCrop === null;
|
|
42771
|
+
var parseSortableCycleTime = (value) => {
|
|
42772
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
42773
|
+
return value;
|
|
42774
|
+
}
|
|
42775
|
+
if (typeof value === "string") {
|
|
42776
|
+
const parsed = Number(value);
|
|
42777
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
42778
|
+
}
|
|
42779
|
+
return null;
|
|
42780
|
+
};
|
|
42781
|
+
var readSortableCycleTime = (clip) => {
|
|
42782
|
+
const candidate = clip;
|
|
42783
|
+
return parseSortableCycleTime(candidate?.cycleTimeSeconds) ?? parseSortableCycleTime(candidate?.cycle_time_seconds) ?? parseSortableCycleTime(candidate?.duration) ?? parseSortableCycleTime(
|
|
42784
|
+
candidate?.original_task_metadata?.cycle_time
|
|
42785
|
+
) ?? null;
|
|
42786
|
+
};
|
|
42787
|
+
var readSortableTimestamp = (clip) => {
|
|
42788
|
+
const candidate = clip;
|
|
42789
|
+
const timestamp = candidate?.creation_timestamp || candidate?.timestamp || candidate?.clip_timestamp || candidate?.clip_end_time || candidate?.clip_start_time;
|
|
42790
|
+
if (typeof timestamp !== "string") {
|
|
42791
|
+
return 0;
|
|
42792
|
+
}
|
|
42793
|
+
const parsed = new Date(timestamp).getTime();
|
|
42794
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
42795
|
+
};
|
|
42796
|
+
var sortPercentileCycleClipsForDisplay = (categoryId, clips) => {
|
|
42797
|
+
if (categoryId !== "fast-cycles" && categoryId !== "slow-cycles") {
|
|
42798
|
+
return [...clips];
|
|
42799
|
+
}
|
|
42800
|
+
return [...clips].sort((left, right) => {
|
|
42801
|
+
const leftCycleTime = readSortableCycleTime(left);
|
|
42802
|
+
const rightCycleTime = readSortableCycleTime(right);
|
|
42803
|
+
const leftMissingCycleTime = leftCycleTime === null;
|
|
42804
|
+
const rightMissingCycleTime = rightCycleTime === null;
|
|
42805
|
+
if (leftMissingCycleTime !== rightMissingCycleTime) {
|
|
42806
|
+
return leftMissingCycleTime ? 1 : -1;
|
|
42807
|
+
}
|
|
42808
|
+
if (leftCycleTime !== null && rightCycleTime !== null && leftCycleTime !== rightCycleTime) {
|
|
42809
|
+
return categoryId === "fast-cycles" ? leftCycleTime - rightCycleTime : rightCycleTime - leftCycleTime;
|
|
42810
|
+
}
|
|
42811
|
+
return readSortableTimestamp(right) - readSortableTimestamp(left);
|
|
42812
|
+
});
|
|
42813
|
+
};
|
|
42814
|
+
var resolveInitialClipCategory = (categoryCandidates, clipTypes = [], counts = {}) => {
|
|
42815
|
+
const candidates = Array.from(
|
|
42816
|
+
new Set(
|
|
42817
|
+
categoryCandidates.filter((candidate) => Boolean(candidate)).map((candidate) => candidate.trim()).filter(Boolean)
|
|
42818
|
+
)
|
|
42819
|
+
);
|
|
42820
|
+
if (candidates.length === 0) {
|
|
42821
|
+
return null;
|
|
42822
|
+
}
|
|
42823
|
+
const availableCategories = /* @__PURE__ */ new Set();
|
|
42824
|
+
clipTypes.forEach((type) => {
|
|
42825
|
+
if (type?.type) availableCategories.add(String(type.type));
|
|
42826
|
+
if (type?.id) availableCategories.add(String(type.id));
|
|
42827
|
+
});
|
|
42828
|
+
return candidates.find((candidate) => (counts[candidate] || 0) > 0) || candidates.find((candidate) => availableCategories.has(candidate)) || null;
|
|
42829
|
+
};
|
|
42392
42830
|
var getCategoryMetadataLoadPlanForFilterChange = ({
|
|
42393
42831
|
activeFilter,
|
|
42394
42832
|
currentClipId,
|
|
@@ -42400,6 +42838,12 @@ var getCategoryMetadataLoadPlanForFilterChange = ({
|
|
|
42400
42838
|
if (activeFilter === "recent_flow_red_streak" && categoryTotal > 0) {
|
|
42401
42839
|
return { shouldLoad: true, autoLoadFirstVideo: true };
|
|
42402
42840
|
}
|
|
42841
|
+
if (activeFilter === "fast-cycles" || activeFilter === "slow-cycles") {
|
|
42842
|
+
return { shouldLoad: true, autoLoadFirstVideo: true };
|
|
42843
|
+
}
|
|
42844
|
+
if (categoryTotal > 0 && activeFilter === "cycle_completion") {
|
|
42845
|
+
return { shouldLoad: true, autoLoadFirstVideo: true };
|
|
42846
|
+
}
|
|
42403
42847
|
return {
|
|
42404
42848
|
shouldLoad: Boolean(currentClipId),
|
|
42405
42849
|
autoLoadFirstVideo: false
|
|
@@ -46237,6 +46681,36 @@ var CLIP_METADATA_PAGE_SIZE = 50;
|
|
|
46237
46681
|
var RECENT_FLOW_RED_STREAK_CLIP_TYPE2 = "recent_flow_red_streak";
|
|
46238
46682
|
var RECENT_FLOW_RED_STREAK_DISPLAY_LABEL = "Low moments";
|
|
46239
46683
|
var RECENT_FLOW_RED_STREAK_DISPLAY_SUBTITLE = "Moments of low efficiency";
|
|
46684
|
+
var REQUIRED_HOURLY_HANDOFF_CATEGORIES = {
|
|
46685
|
+
cycle_completion: {
|
|
46686
|
+
id: "cycle_completion",
|
|
46687
|
+
label: "Cycle Completion",
|
|
46688
|
+
description: "Successfully completed production cycles",
|
|
46689
|
+
color: "green",
|
|
46690
|
+
icon: "check-circle"
|
|
46691
|
+
},
|
|
46692
|
+
idle_time: {
|
|
46693
|
+
id: "idle_time",
|
|
46694
|
+
label: "Idle Time",
|
|
46695
|
+
description: "Idle periods",
|
|
46696
|
+
color: "purple",
|
|
46697
|
+
icon: "clock"
|
|
46698
|
+
},
|
|
46699
|
+
[RECENT_FLOW_RED_STREAK_CLIP_TYPE2]: {
|
|
46700
|
+
id: RECENT_FLOW_RED_STREAK_CLIP_TYPE2,
|
|
46701
|
+
label: RECENT_FLOW_RED_STREAK_DISPLAY_LABEL,
|
|
46702
|
+
description: RECENT_FLOW_RED_STREAK_DISPLAY_SUBTITLE,
|
|
46703
|
+
color: "red",
|
|
46704
|
+
icon: "alert-triangle"
|
|
46705
|
+
}
|
|
46706
|
+
};
|
|
46707
|
+
var REQUIRED_HOURLY_FILTER_CATEGORY_IDS = [
|
|
46708
|
+
RECENT_FLOW_RED_STREAK_CLIP_TYPE2,
|
|
46709
|
+
"cycle_completion",
|
|
46710
|
+
"fast-cycles",
|
|
46711
|
+
"slow-cycles",
|
|
46712
|
+
"idle_time"
|
|
46713
|
+
];
|
|
46240
46714
|
var parseCycleTime = (value) => {
|
|
46241
46715
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
46242
46716
|
return value;
|
|
@@ -46265,6 +46739,21 @@ var formatDurationLabel = (seconds) => {
|
|
|
46265
46739
|
}
|
|
46266
46740
|
return `${Math.round(roundedSeconds / 60)} min`;
|
|
46267
46741
|
};
|
|
46742
|
+
var timeValueToMinutes = (value) => {
|
|
46743
|
+
const [hourValue, minuteValue] = value.substring(0, 5).split(":").map(Number);
|
|
46744
|
+
if (!Number.isInteger(hourValue) || !Number.isInteger(minuteValue) || hourValue < 0 || hourValue > 23 || minuteValue < 0 || minuteValue > 59) {
|
|
46745
|
+
return null;
|
|
46746
|
+
}
|
|
46747
|
+
return hourValue * 60 + minuteValue;
|
|
46748
|
+
};
|
|
46749
|
+
var isMinuteInTimeWindow = (minute, startValue, endValue) => {
|
|
46750
|
+
const startMinute = timeValueToMinutes(startValue);
|
|
46751
|
+
const endMinute = timeValueToMinutes(endValue);
|
|
46752
|
+
if (startMinute === null || endMinute === null) {
|
|
46753
|
+
return true;
|
|
46754
|
+
}
|
|
46755
|
+
return endMinute > startMinute ? minute >= startMinute && minute < endMinute : minute >= startMinute || minute < endMinute;
|
|
46756
|
+
};
|
|
46268
46757
|
var sortRedFlowMetadata = (clips) => {
|
|
46269
46758
|
return clips.slice().sort((left, right) => {
|
|
46270
46759
|
const getOutputShortfall = (clip) => {
|
|
@@ -46289,6 +46778,27 @@ var sortRedFlowMetadata = (clips) => {
|
|
|
46289
46778
|
return (Number.isFinite(rightTime) ? rightTime : 0) - (Number.isFinite(leftTime) ? leftTime : 0);
|
|
46290
46779
|
});
|
|
46291
46780
|
};
|
|
46781
|
+
var buildClipMetadataFromVideo = (video, index) => ({
|
|
46782
|
+
id: video.id,
|
|
46783
|
+
clipId: video.id,
|
|
46784
|
+
clip_timestamp: video.creation_timestamp || video.timestamp,
|
|
46785
|
+
description: video.description,
|
|
46786
|
+
severity: video.severity,
|
|
46787
|
+
category: video.type,
|
|
46788
|
+
duration: typeof video.duration === "number" ? video.duration : typeof video.cycle_time_seconds === "number" ? video.cycle_time_seconds : void 0,
|
|
46789
|
+
clip_start_time: video.clip_start_time,
|
|
46790
|
+
clip_end_time: video.clip_end_time,
|
|
46791
|
+
index,
|
|
46792
|
+
idle_start_time: video.idle_start_time,
|
|
46793
|
+
idle_end_time: video.idle_end_time,
|
|
46794
|
+
cycle_item_count: null,
|
|
46795
|
+
red_flow_timeline: video.red_flow_timeline,
|
|
46796
|
+
red_flow_severity_score: video.red_flow_severity_score,
|
|
46797
|
+
red_flow_output_shortfall_units: video.red_flow_output_shortfall_units,
|
|
46798
|
+
red_flow_worst_minute: video.red_flow_worst_minute,
|
|
46799
|
+
red_flow_explanation_summary: video.red_flow_explanation_summary,
|
|
46800
|
+
red_flow_explanation: video.red_flow_explanation
|
|
46801
|
+
});
|
|
46292
46802
|
var getSeverityIcon = (severity, categoryId, cycleTimeSeconds, targetCycleTime, clipId) => {
|
|
46293
46803
|
if (categoryId === "idle_time" || categoryId === "low_value" || categoryId === "longest-idles") {
|
|
46294
46804
|
return null;
|
|
@@ -46367,6 +46877,8 @@ var FileManagerFilters = ({
|
|
|
46367
46877
|
idleTimeVlmEnabled = false,
|
|
46368
46878
|
showPercentileCycleFilters = true,
|
|
46369
46879
|
prefetchedClipMetadata,
|
|
46880
|
+
prefetchedClipTotals,
|
|
46881
|
+
prefetchedPercentileClips,
|
|
46370
46882
|
externallyManagedLoadingCategories,
|
|
46371
46883
|
activeCategoryLoading,
|
|
46372
46884
|
idleClipSort = "latest",
|
|
@@ -46374,6 +46886,7 @@ var FileManagerFilters = ({
|
|
|
46374
46886
|
initialTimeFilter
|
|
46375
46887
|
}) => {
|
|
46376
46888
|
const [expandedNodes, setExpandedNodes] = React125.useState(/* @__PURE__ */ new Set());
|
|
46889
|
+
const [activeInitialTimeFilter, setActiveInitialTimeFilter] = React125.useState(initialTimeFilter ?? null);
|
|
46377
46890
|
const [startTime, setStartTime] = React125.useState(initialTimeFilter?.startTime ?? "");
|
|
46378
46891
|
const [endTime, setEndTime] = React125.useState(initialTimeFilter?.endTime ?? "");
|
|
46379
46892
|
const [isTimeFilterActive, setIsTimeFilterActive] = React125.useState(
|
|
@@ -46388,14 +46901,93 @@ var FileManagerFilters = ({
|
|
|
46388
46901
|
const [showIdleLabelFilterModal, setShowIdleLabelFilterModal] = React125.useState(false);
|
|
46389
46902
|
const [isLoadingIdleReasonOptions, setIsLoadingIdleReasonOptions] = React125.useState(false);
|
|
46390
46903
|
const timezone = useAppTimezone();
|
|
46904
|
+
const activeTimeFilterTimezone = activeInitialTimeFilter?.timezone || initialTimeFilter?.timezone || timezone;
|
|
46905
|
+
const activeTimeFilterKey = React125.useMemo(() => startTime && endTime && isTimeFilterActive ? `${startTime}-${endTime}-${activeTimeFilterTimezone}` : "none", [activeTimeFilterTimezone, endTime, isTimeFilterActive, startTime]);
|
|
46906
|
+
const initialTimeFilterCategoryIds = React125.useMemo(() => {
|
|
46907
|
+
if (!activeInitialTimeFilter) {
|
|
46908
|
+
return [];
|
|
46909
|
+
}
|
|
46910
|
+
return Array.from(new Set([
|
|
46911
|
+
activeInitialTimeFilter.categoryId,
|
|
46912
|
+
...activeInitialTimeFilter.categoryIds || []
|
|
46913
|
+
].filter((value) => typeof value === "string" && value.length > 0)));
|
|
46914
|
+
}, [activeInitialTimeFilter]);
|
|
46915
|
+
const requiredHourlyFilterCategoryIds = React125.useMemo(() => {
|
|
46916
|
+
if (initialTimeFilterCategoryIds.length > 0) {
|
|
46917
|
+
return initialTimeFilterCategoryIds;
|
|
46918
|
+
}
|
|
46919
|
+
if (!isTimeFilterActive || !startTime || !endTime) {
|
|
46920
|
+
return [];
|
|
46921
|
+
}
|
|
46922
|
+
return REQUIRED_HOURLY_FILTER_CATEGORY_IDS;
|
|
46923
|
+
}, [endTime, initialTimeFilterCategoryIds, isTimeFilterActive, startTime]);
|
|
46924
|
+
const categoriesForTree = React125.useMemo(() => {
|
|
46925
|
+
if (requiredHourlyFilterCategoryIds.length === 0) {
|
|
46926
|
+
return categories;
|
|
46927
|
+
}
|
|
46928
|
+
const existingCategoryIds = new Set(categories.map((category) => category.id));
|
|
46929
|
+
const mergedCategories = [...categories];
|
|
46930
|
+
requiredHourlyFilterCategoryIds.forEach((categoryId) => {
|
|
46931
|
+
const fallbackCategory = REQUIRED_HOURLY_HANDOFF_CATEGORIES[categoryId];
|
|
46932
|
+
if (!fallbackCategory || existingCategoryIds.has(categoryId)) {
|
|
46933
|
+
return;
|
|
46934
|
+
}
|
|
46935
|
+
mergedCategories.push(fallbackCategory);
|
|
46936
|
+
existingCategoryIds.add(categoryId);
|
|
46937
|
+
});
|
|
46938
|
+
return mergedCategories;
|
|
46939
|
+
}, [categories, requiredHourlyFilterCategoryIds]);
|
|
46391
46940
|
const supabase = useSupabase();
|
|
46392
46941
|
const [clipMetadata, setClipMetadata] = React125.useState({});
|
|
46942
|
+
const [scopedCategoryTotals, setScopedCategoryTotals] = React125.useState({});
|
|
46393
46943
|
const [loadingCategories, setLoadingCategories] = React125.useState(/* @__PURE__ */ new Set());
|
|
46944
|
+
const isCategoryScopedByTimeFilter = React125.useCallback((categoryId) => {
|
|
46945
|
+
if (!startTime || !endTime || !isTimeFilterActive) {
|
|
46946
|
+
return false;
|
|
46947
|
+
}
|
|
46948
|
+
if (!activeInitialTimeFilter) {
|
|
46949
|
+
return true;
|
|
46950
|
+
}
|
|
46951
|
+
if (activeFilter === categoryId || activeInitialTimeFilter.categoryId === categoryId) {
|
|
46952
|
+
return true;
|
|
46953
|
+
}
|
|
46954
|
+
return Boolean(activeInitialTimeFilter.categoryIds?.includes(categoryId));
|
|
46955
|
+
}, [
|
|
46956
|
+
activeFilter,
|
|
46957
|
+
activeInitialTimeFilter,
|
|
46958
|
+
endTime,
|
|
46959
|
+
isTimeFilterActive,
|
|
46960
|
+
startTime
|
|
46961
|
+
]);
|
|
46962
|
+
const isRequiredHourlyFilterCategory = React125.useCallback((categoryId) => requiredHourlyFilterCategoryIds.includes(categoryId), [requiredHourlyFilterCategoryIds]);
|
|
46963
|
+
const manualHourlySnapshotKey = React125.useMemo(() => {
|
|
46964
|
+
if (activeInitialTimeFilter || !isTimeFilterActive || !startTime || !endTime || !workspaceId || !date || shift === void 0) {
|
|
46965
|
+
return null;
|
|
46966
|
+
}
|
|
46967
|
+
return `${workspaceId}:${date}:${shift}:${startTime}:${endTime}:${activeTimeFilterTimezone}`;
|
|
46968
|
+
}, [
|
|
46969
|
+
activeInitialTimeFilter,
|
|
46970
|
+
activeTimeFilterTimezone,
|
|
46971
|
+
date,
|
|
46972
|
+
endTime,
|
|
46973
|
+
isTimeFilterActive,
|
|
46974
|
+
shift,
|
|
46975
|
+
startTime,
|
|
46976
|
+
workspaceId
|
|
46977
|
+
]);
|
|
46978
|
+
const manualHourlySnapshotAppliedKeyRef = React125.useRef(null);
|
|
46979
|
+
const manualHourlySnapshotInFlightKeyRef = React125.useRef(null);
|
|
46980
|
+
const [manualHourlySnapshotFailureKey, setManualHourlySnapshotFailureKey] = React125.useState(null);
|
|
46981
|
+
const isManualHourlySnapshotPending = Boolean(
|
|
46982
|
+
manualHourlySnapshotKey && manualHourlySnapshotAppliedKeyRef.current !== manualHourlySnapshotKey && manualHourlySnapshotFailureKey !== manualHourlySnapshotKey
|
|
46983
|
+
);
|
|
46394
46984
|
const [categoryPages, setCategoryPages] = React125.useState({});
|
|
46395
46985
|
const [categoryHasMore, setCategoryHasMore] = React125.useState({});
|
|
46396
46986
|
const [localClipClassifications, setLocalClipClassifications] = React125.useState({});
|
|
46397
46987
|
const clipMetadataRef = React125.useRef({});
|
|
46398
46988
|
const inFlightMetadataRequestsRef = React125.useRef(/* @__PURE__ */ new Set());
|
|
46989
|
+
const inFlightPercentileClipRequestsRef = React125.useRef(/* @__PURE__ */ new Set());
|
|
46990
|
+
const autoSelectedScopedClipRef = React125.useRef(null);
|
|
46399
46991
|
const previousIdleClipSortRef = React125.useRef(idleClipSort);
|
|
46400
46992
|
const mergedClipClassifications = React125.useMemo(() => ({
|
|
46401
46993
|
...clipClassifications || {},
|
|
@@ -46408,12 +47000,14 @@ var FileManagerFilters = ({
|
|
|
46408
47000
|
if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime) {
|
|
46409
47001
|
return;
|
|
46410
47002
|
}
|
|
47003
|
+
setActiveInitialTimeFilter(initialTimeFilter);
|
|
46411
47004
|
setStartTime(initialTimeFilter.startTime);
|
|
46412
47005
|
setEndTime(initialTimeFilter.endTime);
|
|
46413
47006
|
setIsTimeFilterActive(true);
|
|
46414
47007
|
setShowTimeFilterModal(false);
|
|
46415
47008
|
setStartSearchTerm("");
|
|
46416
47009
|
setEndSearchTerm("");
|
|
47010
|
+
setScopedCategoryTotals({});
|
|
46417
47011
|
}, [initialTimeFilter?.startTime, initialTimeFilter?.endTime]);
|
|
46418
47012
|
React125.useEffect(() => {
|
|
46419
47013
|
if (previousIdleClipSortRef.current === idleClipSort) {
|
|
@@ -46445,18 +47039,66 @@ var FileManagerFilters = ({
|
|
|
46445
47039
|
return next;
|
|
46446
47040
|
});
|
|
46447
47041
|
}, [idleClipSort]);
|
|
47042
|
+
React125.useEffect(() => {
|
|
47043
|
+
manualHourlySnapshotAppliedKeyRef.current = null;
|
|
47044
|
+
manualHourlySnapshotInFlightKeyRef.current = null;
|
|
47045
|
+
setManualHourlySnapshotFailureKey(null);
|
|
47046
|
+
setScopedCategoryTotals({});
|
|
47047
|
+
setClipMetadata({});
|
|
47048
|
+
setCategoryPages({});
|
|
47049
|
+
setCategoryHasMore({});
|
|
47050
|
+
setPercentileClips({});
|
|
47051
|
+
setPercentileCounts({
|
|
47052
|
+
"fast-cycles": null,
|
|
47053
|
+
"slow-cycles": null
|
|
47054
|
+
});
|
|
47055
|
+
}, [activeTimeFilterKey]);
|
|
46448
47056
|
const isCategoryExternallyManaged = React125.useCallback((categoryId) => {
|
|
46449
47057
|
if (!categoryId) {
|
|
46450
47058
|
return false;
|
|
46451
47059
|
}
|
|
46452
|
-
if (
|
|
47060
|
+
if (isManualHourlySnapshotPending && requiredHourlyFilterCategoryIds.includes(categoryId)) {
|
|
47061
|
+
return true;
|
|
47062
|
+
}
|
|
47063
|
+
if (prefetchedClipMetadata && Array.isArray(prefetchedClipMetadata[categoryId]) && (prefetchedClipMetadata[categoryId].length > 0 || typeof prefetchedClipTotals?.[categoryId] === "number")) {
|
|
47064
|
+
return true;
|
|
47065
|
+
}
|
|
47066
|
+
if (prefetchedPercentileClips && Array.isArray(prefetchedPercentileClips[categoryId]) && (prefetchedPercentileClips[categoryId].length > 0 || isCategoryScopedByTimeFilter(categoryId) && typeof prefetchedClipTotals?.[categoryId] === "number")) {
|
|
46453
47067
|
return true;
|
|
46454
47068
|
}
|
|
46455
47069
|
if (externallyManagedLoadingCategories?.[categoryId]) {
|
|
46456
47070
|
return true;
|
|
46457
47071
|
}
|
|
46458
47072
|
return false;
|
|
46459
|
-
}, [
|
|
47073
|
+
}, [
|
|
47074
|
+
externallyManagedLoadingCategories,
|
|
47075
|
+
isManualHourlySnapshotPending,
|
|
47076
|
+
isCategoryScopedByTimeFilter,
|
|
47077
|
+
prefetchedClipMetadata,
|
|
47078
|
+
prefetchedClipTotals,
|
|
47079
|
+
prefetchedPercentileClips,
|
|
47080
|
+
requiredHourlyFilterCategoryIds
|
|
47081
|
+
]);
|
|
47082
|
+
const getAvailableClipMetadata = React125.useCallback((categoryId) => {
|
|
47083
|
+
const localClips = clipMetadata[categoryId];
|
|
47084
|
+
if (Array.isArray(localClips) && localClips.length > 0) {
|
|
47085
|
+
return localClips;
|
|
47086
|
+
}
|
|
47087
|
+
const prefetchedClips = prefetchedClipMetadata?.[categoryId];
|
|
47088
|
+
if (Array.isArray(prefetchedClips) && prefetchedClips.length > 0) {
|
|
47089
|
+
return prefetchedClips;
|
|
47090
|
+
}
|
|
47091
|
+
return [];
|
|
47092
|
+
}, [clipMetadata, prefetchedClipMetadata]);
|
|
47093
|
+
const hasKnownClipMetadata = React125.useCallback((categoryId) => {
|
|
47094
|
+
if (Array.isArray(clipMetadata[categoryId])) {
|
|
47095
|
+
return true;
|
|
47096
|
+
}
|
|
47097
|
+
if (Array.isArray(prefetchedClipMetadata?.[categoryId])) {
|
|
47098
|
+
return true;
|
|
47099
|
+
}
|
|
47100
|
+
return typeof prefetchedClipTotals?.[categoryId] === "number";
|
|
47101
|
+
}, [clipMetadata, prefetchedClipMetadata, prefetchedClipTotals]);
|
|
46460
47102
|
React125.useEffect(() => {
|
|
46461
47103
|
if (!prefetchedClipMetadata) {
|
|
46462
47104
|
return;
|
|
@@ -46479,6 +47121,22 @@ var FileManagerFilters = ({
|
|
|
46479
47121
|
});
|
|
46480
47122
|
return changed ? next : prev;
|
|
46481
47123
|
});
|
|
47124
|
+
setScopedCategoryTotals((prev) => {
|
|
47125
|
+
let changed = false;
|
|
47126
|
+
const next = { ...prev };
|
|
47127
|
+
Object.entries(prefetchedClipMetadata).forEach(([categoryId, clips]) => {
|
|
47128
|
+
if (!Array.isArray(clips) || !isCategoryScopedByTimeFilter(categoryId)) {
|
|
47129
|
+
return;
|
|
47130
|
+
}
|
|
47131
|
+
const nextTotal = typeof prefetchedClipTotals?.[categoryId] === "number" ? Math.max(0, prefetchedClipTotals[categoryId]) : clips.length;
|
|
47132
|
+
if (next[categoryId] === nextTotal) {
|
|
47133
|
+
return;
|
|
47134
|
+
}
|
|
47135
|
+
next[categoryId] = nextTotal;
|
|
47136
|
+
changed = true;
|
|
47137
|
+
});
|
|
47138
|
+
return changed ? next : prev;
|
|
47139
|
+
});
|
|
46482
47140
|
setCategoryPages((prev) => {
|
|
46483
47141
|
let changed = false;
|
|
46484
47142
|
const next = { ...prev };
|
|
@@ -46503,7 +47161,7 @@ var FileManagerFilters = ({
|
|
|
46503
47161
|
return;
|
|
46504
47162
|
}
|
|
46505
47163
|
const knownTotal = typeof counts?.[categoryId] === "number" ? counts[categoryId] : null;
|
|
46506
|
-
const inferredHasMore = knownTotal !== null ? clips.length < knownTotal : prev[categoryId];
|
|
47164
|
+
const inferredHasMore = isCategoryScopedByTimeFilter(categoryId) ? false : knownTotal !== null ? clips.length < knownTotal : prev[categoryId];
|
|
46507
47165
|
if (typeof inferredHasMore !== "boolean" || next[categoryId] === inferredHasMore) {
|
|
46508
47166
|
return;
|
|
46509
47167
|
}
|
|
@@ -46512,8 +47170,68 @@ var FileManagerFilters = ({
|
|
|
46512
47170
|
});
|
|
46513
47171
|
return changed ? next : prev;
|
|
46514
47172
|
});
|
|
46515
|
-
}, [prefetchedClipMetadata, counts]);
|
|
47173
|
+
}, [prefetchedClipMetadata, prefetchedClipTotals, counts, isCategoryScopedByTimeFilter]);
|
|
47174
|
+
React125.useEffect(() => {
|
|
47175
|
+
if (!prefetchedPercentileClips) {
|
|
47176
|
+
return;
|
|
47177
|
+
}
|
|
47178
|
+
setPercentileClips((prev) => {
|
|
47179
|
+
let changed = false;
|
|
47180
|
+
const next = { ...prev };
|
|
47181
|
+
["fast-cycles", "slow-cycles"].forEach((categoryId) => {
|
|
47182
|
+
const clips = prefetchedPercentileClips[categoryId];
|
|
47183
|
+
if (!Array.isArray(clips)) {
|
|
47184
|
+
return;
|
|
47185
|
+
}
|
|
47186
|
+
const previousClips = prev[categoryId] || [];
|
|
47187
|
+
const previousSignature = previousClips.map((clip) => clip.id).join("|");
|
|
47188
|
+
const nextSignature = clips.map((clip) => clip.id).join("|");
|
|
47189
|
+
if (previousSignature === nextSignature) {
|
|
47190
|
+
return;
|
|
47191
|
+
}
|
|
47192
|
+
next[categoryId] = clips;
|
|
47193
|
+
changed = true;
|
|
47194
|
+
});
|
|
47195
|
+
return changed ? next : prev;
|
|
47196
|
+
});
|
|
47197
|
+
setPercentileCounts((prev) => {
|
|
47198
|
+
let changed = false;
|
|
47199
|
+
const next = { ...prev };
|
|
47200
|
+
["fast-cycles", "slow-cycles"].forEach((categoryId) => {
|
|
47201
|
+
const clips = prefetchedPercentileClips[categoryId];
|
|
47202
|
+
if (!Array.isArray(clips)) {
|
|
47203
|
+
return;
|
|
47204
|
+
}
|
|
47205
|
+
const nextTotal = typeof prefetchedClipTotals?.[categoryId] === "number" ? Math.max(0, prefetchedClipTotals[categoryId]) : clips.length;
|
|
47206
|
+
if (next[categoryId] === nextTotal) {
|
|
47207
|
+
return;
|
|
47208
|
+
}
|
|
47209
|
+
next[categoryId] = nextTotal;
|
|
47210
|
+
changed = true;
|
|
47211
|
+
});
|
|
47212
|
+
return changed ? next : prev;
|
|
47213
|
+
});
|
|
47214
|
+
}, [prefetchedClipTotals, prefetchedPercentileClips]);
|
|
46516
47215
|
const { state: filterState } = useClipFilter();
|
|
47216
|
+
const shouldShowCategory = React125.useCallback((categoryId) => {
|
|
47217
|
+
switch (categoryId) {
|
|
47218
|
+
case "fast-cycles":
|
|
47219
|
+
return showPercentileCycleFilters && filterState.showFastCycles;
|
|
47220
|
+
case "slow-cycles":
|
|
47221
|
+
return showPercentileCycleFilters && filterState.showSlowCycles;
|
|
47222
|
+
case "longest-idles":
|
|
47223
|
+
return false;
|
|
47224
|
+
// filterState.showLongestIdles; // Temporarily disabled
|
|
47225
|
+
case "cycle_completion":
|
|
47226
|
+
return filterState.showCycleCompletion;
|
|
47227
|
+
case "idle_time":
|
|
47228
|
+
return filterState.showIdleTime;
|
|
47229
|
+
case "sop_deviations":
|
|
47230
|
+
return filterState.showSopDeviations;
|
|
47231
|
+
default:
|
|
47232
|
+
return true;
|
|
47233
|
+
}
|
|
47234
|
+
}, [filterState, showPercentileCycleFilters]);
|
|
46517
47235
|
const [percentileCounts, setPercentileCounts] = React125.useState({
|
|
46518
47236
|
"fast-cycles": null,
|
|
46519
47237
|
"slow-cycles": null
|
|
@@ -46623,11 +47341,12 @@ var FileManagerFilters = ({
|
|
|
46623
47341
|
return null;
|
|
46624
47342
|
}
|
|
46625
47343
|
}, [supabase]);
|
|
46626
|
-
const getMetadataLoadingKey = React125.useCallback((categoryId, page) => `${categoryId}-${page}-${categoryId === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"}`, [idleClipSort]);
|
|
47344
|
+
const getMetadataLoadingKey = React125.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]);
|
|
46627
47345
|
const fetchClipMetadataPage = React125.useCallback(async (categoryId, page = 1) => {
|
|
46628
47346
|
if (!workspaceId || !date || shift === void 0) {
|
|
46629
47347
|
throw new Error("Missing required params for clip metadata fetch");
|
|
46630
47348
|
}
|
|
47349
|
+
const shouldScopeToTimeFilter = isCategoryScopedByTimeFilter(categoryId);
|
|
46631
47350
|
const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
|
|
46632
47351
|
method: "POST",
|
|
46633
47352
|
headers: {
|
|
@@ -46641,9 +47360,12 @@ var FileManagerFilters = ({
|
|
|
46641
47360
|
category: categoryId,
|
|
46642
47361
|
page,
|
|
46643
47362
|
limit: CLIP_METADATA_PAGE_SIZE,
|
|
46644
|
-
knownTotal: typeof counts?.[categoryId] === "number" ? counts[categoryId] : null,
|
|
47363
|
+
knownTotal: shouldScopeToTimeFilter ? null : typeof counts?.[categoryId] === "number" ? counts[categoryId] : null,
|
|
46645
47364
|
snapshotDateTime,
|
|
46646
47365
|
snapshotClipId,
|
|
47366
|
+
startTime: shouldScopeToTimeFilter ? startTime : void 0,
|
|
47367
|
+
endTime: shouldScopeToTimeFilter ? endTime : void 0,
|
|
47368
|
+
timeFilterTimezone: shouldScopeToTimeFilter ? activeTimeFilterTimezone : void 0,
|
|
46647
47369
|
sort: categoryId === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
|
|
46648
47370
|
}),
|
|
46649
47371
|
redirectReason: "session_expired"
|
|
@@ -46652,7 +47374,20 @@ var FileManagerFilters = ({
|
|
|
46652
47374
|
throw new Error(`API error: ${response.status}`);
|
|
46653
47375
|
}
|
|
46654
47376
|
return response.json();
|
|
46655
|
-
}, [
|
|
47377
|
+
}, [
|
|
47378
|
+
activeTimeFilterTimezone,
|
|
47379
|
+
endTime,
|
|
47380
|
+
isCategoryScopedByTimeFilter,
|
|
47381
|
+
startTime,
|
|
47382
|
+
workspaceId,
|
|
47383
|
+
date,
|
|
47384
|
+
shift,
|
|
47385
|
+
counts,
|
|
47386
|
+
snapshotDateTime,
|
|
47387
|
+
snapshotClipId,
|
|
47388
|
+
idleClipSort,
|
|
47389
|
+
supabase
|
|
47390
|
+
]);
|
|
46656
47391
|
const seedIdleClassifications = React125.useCallback(async (clips) => {
|
|
46657
47392
|
if (!idleTimeVlmEnabled || clips.length === 0) {
|
|
46658
47393
|
return;
|
|
@@ -46720,6 +47455,12 @@ var FileManagerFilters = ({
|
|
|
46720
47455
|
...prev,
|
|
46721
47456
|
[categoryId]: page === 1 ? data.clips : [...prev[categoryId] || [], ...data.clips]
|
|
46722
47457
|
}));
|
|
47458
|
+
if (isCategoryScopedByTimeFilter(categoryId)) {
|
|
47459
|
+
setScopedCategoryTotals((prev) => ({
|
|
47460
|
+
...prev,
|
|
47461
|
+
[categoryId]: Math.max(0, Number(data.total || 0))
|
|
47462
|
+
}));
|
|
47463
|
+
}
|
|
46723
47464
|
if (categoryId === "idle_time" && idleTimeVlmEnabled) {
|
|
46724
47465
|
await seedIdleClassifications(data.clips || []);
|
|
46725
47466
|
}
|
|
@@ -46736,7 +47477,94 @@ var FileManagerFilters = ({
|
|
|
46736
47477
|
return newSet;
|
|
46737
47478
|
});
|
|
46738
47479
|
}
|
|
46739
|
-
}, [workspaceId, date, shift, fetchClipMetadataPage, idleTimeVlmEnabled, seedIdleClassifications, getMetadataLoadingKey]);
|
|
47480
|
+
}, [workspaceId, date, shift, fetchClipMetadataPage, idleTimeVlmEnabled, seedIdleClassifications, getMetadataLoadingKey, isCategoryScopedByTimeFilter]);
|
|
47481
|
+
React125.useCallback(async (categoryId) => {
|
|
47482
|
+
if (!workspaceId || !date || shift === void 0) {
|
|
47483
|
+
console.warn("[FileManager] Missing required params for full clip metadata fetch");
|
|
47484
|
+
return;
|
|
47485
|
+
}
|
|
47486
|
+
const loadingKey = `${getMetadataLoadingKey(categoryId, 1)}-all`;
|
|
47487
|
+
if (inFlightMetadataRequestsRef.current.has(loadingKey)) {
|
|
47488
|
+
return;
|
|
47489
|
+
}
|
|
47490
|
+
inFlightMetadataRequestsRef.current.add(loadingKey);
|
|
47491
|
+
setLoadingCategories((prev) => /* @__PURE__ */ new Set([...prev, loadingKey]));
|
|
47492
|
+
try {
|
|
47493
|
+
let accumulatedClips = [];
|
|
47494
|
+
let currentPage = 0;
|
|
47495
|
+
let hasMore = true;
|
|
47496
|
+
while (hasMore) {
|
|
47497
|
+
const nextPage = currentPage + 1;
|
|
47498
|
+
const pageData = await fetchClipMetadataPage(categoryId, nextPage);
|
|
47499
|
+
const pageClips = pageData.clips || [];
|
|
47500
|
+
accumulatedClips = [...accumulatedClips, ...pageClips];
|
|
47501
|
+
currentPage = nextPage;
|
|
47502
|
+
hasMore = Boolean(pageData.hasMore && pageClips.length > 0);
|
|
47503
|
+
}
|
|
47504
|
+
const dedupedClips = accumulatedClips.filter((clip, index, arr) => {
|
|
47505
|
+
const clipKey = clip.clipId || clip.id;
|
|
47506
|
+
return arr.findIndex((item) => (item.clipId || item.id) === clipKey) === index;
|
|
47507
|
+
});
|
|
47508
|
+
setClipMetadata((prev) => ({
|
|
47509
|
+
...prev,
|
|
47510
|
+
[categoryId]: dedupedClips
|
|
47511
|
+
}));
|
|
47512
|
+
setCategoryPages((prev) => ({ ...prev, [categoryId]: Math.max(currentPage, 1) }));
|
|
47513
|
+
setCategoryHasMore((prev) => ({ ...prev, [categoryId]: false }));
|
|
47514
|
+
if (categoryId === "idle_time" && idleTimeVlmEnabled) {
|
|
47515
|
+
await seedIdleClassifications(dedupedClips);
|
|
47516
|
+
}
|
|
47517
|
+
console.log(`[FileManager] Loaded all ${dedupedClips.length} clips for ${categoryId} due to chart time filter`);
|
|
47518
|
+
} catch (error) {
|
|
47519
|
+
console.error("[FileManager] Error fetching full clip metadata:", error);
|
|
47520
|
+
} finally {
|
|
47521
|
+
inFlightMetadataRequestsRef.current.delete(loadingKey);
|
|
47522
|
+
setLoadingCategories((prev) => {
|
|
47523
|
+
const newSet = new Set(prev);
|
|
47524
|
+
newSet.delete(loadingKey);
|
|
47525
|
+
return newSet;
|
|
47526
|
+
});
|
|
47527
|
+
}
|
|
47528
|
+
}, [
|
|
47529
|
+
workspaceId,
|
|
47530
|
+
date,
|
|
47531
|
+
shift,
|
|
47532
|
+
fetchClipMetadataPage,
|
|
47533
|
+
idleTimeVlmEnabled,
|
|
47534
|
+
seedIdleClassifications,
|
|
47535
|
+
getMetadataLoadingKey
|
|
47536
|
+
]);
|
|
47537
|
+
const isInitialTimeFilterCategory = React125.useCallback((categoryId) => {
|
|
47538
|
+
if (!startTime || !endTime || !activeInitialTimeFilter) {
|
|
47539
|
+
return false;
|
|
47540
|
+
}
|
|
47541
|
+
if (activeFilter === categoryId) {
|
|
47542
|
+
return true;
|
|
47543
|
+
}
|
|
47544
|
+
if (activeInitialTimeFilter.categoryId === categoryId) {
|
|
47545
|
+
return true;
|
|
47546
|
+
}
|
|
47547
|
+
return Array.isArray(activeInitialTimeFilter.categoryIds) && activeInitialTimeFilter.categoryIds.includes(categoryId);
|
|
47548
|
+
}, [
|
|
47549
|
+
activeFilter,
|
|
47550
|
+
activeInitialTimeFilter,
|
|
47551
|
+
endTime,
|
|
47552
|
+
startTime
|
|
47553
|
+
]);
|
|
47554
|
+
const hasCompleteMetadataForInitialTimeFilter = React125.useCallback((categoryId) => {
|
|
47555
|
+
if (!isInitialTimeFilterCategory(categoryId)) {
|
|
47556
|
+
return true;
|
|
47557
|
+
}
|
|
47558
|
+
const loadedClips = clipMetadataRef.current[categoryId] || [];
|
|
47559
|
+
const knownTotal = typeof scopedCategoryTotals[categoryId] === "number" ? scopedCategoryTotals[categoryId] : null;
|
|
47560
|
+
if (categoryHasMore[categoryId]) {
|
|
47561
|
+
return false;
|
|
47562
|
+
}
|
|
47563
|
+
if (knownTotal !== null && loadedClips.length < knownTotal) {
|
|
47564
|
+
return false;
|
|
47565
|
+
}
|
|
47566
|
+
return loadedClips.length > 0 || knownTotal === 0;
|
|
47567
|
+
}, [categoryHasMore, isInitialTimeFilterCategory, scopedCategoryTotals]);
|
|
46740
47568
|
const ensureAllIdleTimeClipMetadataLoaded = React125.useCallback(async () => {
|
|
46741
47569
|
if (!workspaceId || !date || shift === void 0) {
|
|
46742
47570
|
return;
|
|
@@ -46802,6 +47630,12 @@ var FileManagerFilters = ({
|
|
|
46802
47630
|
console.warn("[FileManager] Missing required params for percentile clips fetch");
|
|
46803
47631
|
return;
|
|
46804
47632
|
}
|
|
47633
|
+
const shouldScopeToTimeFilter = isCategoryScopedByTimeFilter(type);
|
|
47634
|
+
const requestKey = `${type}:${date}:${shift}:${filterState.percentile}:${shouldScopeToTimeFilter ? activeTimeFilterKey : "unscoped"}`;
|
|
47635
|
+
if (inFlightPercentileClipRequestsRef.current.has(requestKey)) {
|
|
47636
|
+
return;
|
|
47637
|
+
}
|
|
47638
|
+
inFlightPercentileClipRequestsRef.current.add(requestKey);
|
|
46805
47639
|
try {
|
|
46806
47640
|
const startDate = `${date}T00:00:00Z`;
|
|
46807
47641
|
const endDate = `${date}T23:59:59Z`;
|
|
@@ -46817,7 +47651,10 @@ var FileManagerFilters = ({
|
|
|
46817
47651
|
endDate,
|
|
46818
47652
|
percentile: filterState.percentile,
|
|
46819
47653
|
shiftId: shift,
|
|
46820
|
-
limit:
|
|
47654
|
+
limit: shouldScopeToTimeFilter ? 500 : 100,
|
|
47655
|
+
startTime: shouldScopeToTimeFilter ? startTime : void 0,
|
|
47656
|
+
endTime: shouldScopeToTimeFilter ? endTime : void 0,
|
|
47657
|
+
timeFilterTimezone: shouldScopeToTimeFilter ? activeTimeFilterTimezone : void 0,
|
|
46821
47658
|
// The actual percentile action (fast-cycles, slow-cycles, idle-times)
|
|
46822
47659
|
percentileAction: type
|
|
46823
47660
|
}),
|
|
@@ -46832,7 +47669,7 @@ var FileManagerFilters = ({
|
|
|
46832
47669
|
[type]: data.clips || []
|
|
46833
47670
|
}));
|
|
46834
47671
|
setPercentileCounts((prev) => {
|
|
46835
|
-
if (typeof prev[type] === "number") {
|
|
47672
|
+
if (!shouldScopeToTimeFilter && typeof prev[type] === "number") {
|
|
46836
47673
|
return prev;
|
|
46837
47674
|
}
|
|
46838
47675
|
return {
|
|
@@ -46843,8 +47680,206 @@ var FileManagerFilters = ({
|
|
|
46843
47680
|
console.log(`[FileManager] Loaded ${data.clips?.length || 0} ${type} clips (percentile: ${filterState.percentile}%)`);
|
|
46844
47681
|
} catch (error) {
|
|
46845
47682
|
console.error(`[FileManager] Error fetching ${type} clips:`, error);
|
|
47683
|
+
} finally {
|
|
47684
|
+
inFlightPercentileClipRequestsRef.current.delete(requestKey);
|
|
47685
|
+
}
|
|
47686
|
+
}, [
|
|
47687
|
+
activeTimeFilterTimezone,
|
|
47688
|
+
activeTimeFilterKey,
|
|
47689
|
+
endTime,
|
|
47690
|
+
isCategoryScopedByTimeFilter,
|
|
47691
|
+
startTime,
|
|
47692
|
+
workspaceId,
|
|
47693
|
+
date,
|
|
47694
|
+
shift,
|
|
47695
|
+
filterState.percentile,
|
|
47696
|
+
showPercentileCycleFilters,
|
|
47697
|
+
supabase
|
|
47698
|
+
]);
|
|
47699
|
+
React125.useEffect(() => {
|
|
47700
|
+
if (!manualHourlySnapshotKey) {
|
|
47701
|
+
return;
|
|
47702
|
+
}
|
|
47703
|
+
if (manualHourlySnapshotAppliedKeyRef.current === manualHourlySnapshotKey) {
|
|
47704
|
+
return;
|
|
47705
|
+
}
|
|
47706
|
+
if (manualHourlySnapshotInFlightKeyRef.current === manualHourlySnapshotKey) {
|
|
47707
|
+
return;
|
|
47708
|
+
}
|
|
47709
|
+
manualHourlySnapshotInFlightKeyRef.current = manualHourlySnapshotKey;
|
|
47710
|
+
setManualHourlySnapshotFailureKey((prev) => prev === manualHourlySnapshotKey ? null : prev);
|
|
47711
|
+
const applyHourlySnapshot = async () => {
|
|
47712
|
+
try {
|
|
47713
|
+
const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
|
|
47714
|
+
method: "POST",
|
|
47715
|
+
headers: {
|
|
47716
|
+
"Content-Type": "application/json"
|
|
47717
|
+
},
|
|
47718
|
+
body: JSON.stringify({
|
|
47719
|
+
action: "hourly-snapshot",
|
|
47720
|
+
workspaceId,
|
|
47721
|
+
date,
|
|
47722
|
+
shift,
|
|
47723
|
+
startTime,
|
|
47724
|
+
endTime,
|
|
47725
|
+
timeFilterTimezone: activeTimeFilterTimezone
|
|
47726
|
+
}),
|
|
47727
|
+
redirectReason: "session_expired"
|
|
47728
|
+
});
|
|
47729
|
+
if (!response.ok) {
|
|
47730
|
+
throw new Error(`API error: ${response.status}`);
|
|
47731
|
+
}
|
|
47732
|
+
const snapshot = await response.json();
|
|
47733
|
+
const metadataByCategory = snapshot?.metadataByCategory && typeof snapshot.metadataByCategory === "object" ? snapshot.metadataByCategory : {};
|
|
47734
|
+
const percentileClipsByCategory = snapshot?.percentileClipsByCategory && typeof snapshot.percentileClipsByCategory === "object" ? snapshot.percentileClipsByCategory : {};
|
|
47735
|
+
const rawTotalsByCategory = snapshot?.totalsByCategory && typeof snapshot.totalsByCategory === "object" ? snapshot.totalsByCategory : {};
|
|
47736
|
+
const totalsByCategory = requiredHourlyFilterCategoryIds.reduce((accumulator, categoryId) => {
|
|
47737
|
+
const rawTotal = rawTotalsByCategory[categoryId];
|
|
47738
|
+
if (typeof rawTotal === "number" && Number.isFinite(rawTotal)) {
|
|
47739
|
+
accumulator[categoryId] = Math.max(0, rawTotal);
|
|
47740
|
+
return accumulator;
|
|
47741
|
+
}
|
|
47742
|
+
if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
|
|
47743
|
+
accumulator[categoryId] = Array.isArray(percentileClipsByCategory[categoryId]) ? percentileClipsByCategory[categoryId].length : 0;
|
|
47744
|
+
return accumulator;
|
|
47745
|
+
}
|
|
47746
|
+
accumulator[categoryId] = Array.isArray(metadataByCategory[categoryId]) ? metadataByCategory[categoryId].length : 0;
|
|
47747
|
+
return accumulator;
|
|
47748
|
+
}, {});
|
|
47749
|
+
setClipMetadata((prev) => {
|
|
47750
|
+
const next = { ...prev };
|
|
47751
|
+
requiredHourlyFilterCategoryIds.forEach((categoryId) => {
|
|
47752
|
+
if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
|
|
47753
|
+
return;
|
|
47754
|
+
}
|
|
47755
|
+
next[categoryId] = Array.isArray(metadataByCategory[categoryId]) ? metadataByCategory[categoryId] : [];
|
|
47756
|
+
});
|
|
47757
|
+
return next;
|
|
47758
|
+
});
|
|
47759
|
+
setScopedCategoryTotals((prev) => ({
|
|
47760
|
+
...prev,
|
|
47761
|
+
...totalsByCategory
|
|
47762
|
+
}));
|
|
47763
|
+
if (showPercentileCycleFilters) {
|
|
47764
|
+
setPercentileClips((prev) => ({
|
|
47765
|
+
...prev,
|
|
47766
|
+
"fast-cycles": Array.isArray(percentileClipsByCategory["fast-cycles"]) ? percentileClipsByCategory["fast-cycles"] : [],
|
|
47767
|
+
"slow-cycles": Array.isArray(percentileClipsByCategory["slow-cycles"]) ? percentileClipsByCategory["slow-cycles"] : []
|
|
47768
|
+
}));
|
|
47769
|
+
setPercentileCounts((prev) => ({
|
|
47770
|
+
...prev,
|
|
47771
|
+
"fast-cycles": totalsByCategory["fast-cycles"] ?? 0,
|
|
47772
|
+
"slow-cycles": totalsByCategory["slow-cycles"] ?? 0
|
|
47773
|
+
}));
|
|
47774
|
+
}
|
|
47775
|
+
setCategoryPages((prev) => {
|
|
47776
|
+
const next = { ...prev };
|
|
47777
|
+
requiredHourlyFilterCategoryIds.forEach((categoryId) => {
|
|
47778
|
+
next[categoryId] = 1;
|
|
47779
|
+
});
|
|
47780
|
+
return next;
|
|
47781
|
+
});
|
|
47782
|
+
setCategoryHasMore((prev) => {
|
|
47783
|
+
const next = { ...prev };
|
|
47784
|
+
requiredHourlyFilterCategoryIds.forEach((categoryId) => {
|
|
47785
|
+
next[categoryId] = false;
|
|
47786
|
+
});
|
|
47787
|
+
return next;
|
|
47788
|
+
});
|
|
47789
|
+
const idleClips = metadataByCategory.idle_time;
|
|
47790
|
+
if (Array.isArray(idleClips) && idleClips.length > 0) {
|
|
47791
|
+
await seedIdleClassifications(idleClips);
|
|
47792
|
+
}
|
|
47793
|
+
manualHourlySnapshotAppliedKeyRef.current = manualHourlySnapshotKey;
|
|
47794
|
+
setManualHourlySnapshotFailureKey((prev) => prev === manualHourlySnapshotKey ? null : prev);
|
|
47795
|
+
} catch (error) {
|
|
47796
|
+
console.error("[FileManager] Error fetching manual hourly snapshot:", error);
|
|
47797
|
+
setManualHourlySnapshotFailureKey(manualHourlySnapshotKey);
|
|
47798
|
+
} finally {
|
|
47799
|
+
if (manualHourlySnapshotInFlightKeyRef.current === manualHourlySnapshotKey) {
|
|
47800
|
+
manualHourlySnapshotInFlightKeyRef.current = null;
|
|
47801
|
+
}
|
|
47802
|
+
}
|
|
47803
|
+
};
|
|
47804
|
+
void applyHourlySnapshot();
|
|
47805
|
+
}, [
|
|
47806
|
+
activeTimeFilterTimezone,
|
|
47807
|
+
date,
|
|
47808
|
+
endTime,
|
|
47809
|
+
manualHourlySnapshotKey,
|
|
47810
|
+
requiredHourlyFilterCategoryIds,
|
|
47811
|
+
seedIdleClassifications,
|
|
47812
|
+
shift,
|
|
47813
|
+
showPercentileCycleFilters,
|
|
47814
|
+
startTime,
|
|
47815
|
+
supabase,
|
|
47816
|
+
workspaceId
|
|
47817
|
+
]);
|
|
47818
|
+
React125.useEffect(() => {
|
|
47819
|
+
if (!startTime || !endTime || !activeInitialTimeFilter || initialTimeFilterCategoryIds.length === 0) {
|
|
47820
|
+
return;
|
|
46846
47821
|
}
|
|
46847
|
-
|
|
47822
|
+
initialTimeFilterCategoryIds.forEach((categoryId) => {
|
|
47823
|
+
if (categoryId === "fast-cycles" || categoryId === "slow-cycles") {
|
|
47824
|
+
const hasScopedPercentileResult = typeof percentileCounts[categoryId] === "number";
|
|
47825
|
+
if (!isCategoryExternallyManaged(categoryId) && !hasScopedPercentileResult && showPercentileCycleFilters) {
|
|
47826
|
+
fetchPercentileClips(categoryId);
|
|
47827
|
+
}
|
|
47828
|
+
return;
|
|
47829
|
+
}
|
|
47830
|
+
if (isCategoryExternallyManaged(categoryId)) {
|
|
47831
|
+
return;
|
|
47832
|
+
}
|
|
47833
|
+
if (!hasCompleteMetadataForInitialTimeFilter(categoryId)) {
|
|
47834
|
+
fetchClipMetadata(categoryId, 1);
|
|
47835
|
+
}
|
|
47836
|
+
});
|
|
47837
|
+
}, [
|
|
47838
|
+
activeInitialTimeFilter,
|
|
47839
|
+
endTime,
|
|
47840
|
+
fetchClipMetadata,
|
|
47841
|
+
fetchPercentileClips,
|
|
47842
|
+
hasCompleteMetadataForInitialTimeFilter,
|
|
47843
|
+
initialTimeFilterCategoryIds,
|
|
47844
|
+
isCategoryExternallyManaged,
|
|
47845
|
+
percentileCounts,
|
|
47846
|
+
showPercentileCycleFilters,
|
|
47847
|
+
startTime
|
|
47848
|
+
]);
|
|
47849
|
+
React125.useEffect(() => {
|
|
47850
|
+
if (!startTime || !endTime || !isTimeFilterActive || activeInitialTimeFilter) {
|
|
47851
|
+
return;
|
|
47852
|
+
}
|
|
47853
|
+
categoriesForTree.forEach((category) => {
|
|
47854
|
+
if (!shouldShowCategory(category.id) || isCategoryExternallyManaged(category.id)) {
|
|
47855
|
+
return;
|
|
47856
|
+
}
|
|
47857
|
+
if (typeof scopedCategoryTotals[category.id] !== "number") {
|
|
47858
|
+
fetchClipMetadata(category.id, 1);
|
|
47859
|
+
}
|
|
47860
|
+
});
|
|
47861
|
+
if (showPercentileCycleFilters) {
|
|
47862
|
+
if (!isCategoryExternallyManaged("fast-cycles") && typeof percentileCounts["fast-cycles"] !== "number") {
|
|
47863
|
+
fetchPercentileClips("fast-cycles");
|
|
47864
|
+
}
|
|
47865
|
+
if (!isCategoryExternallyManaged("slow-cycles") && typeof percentileCounts["slow-cycles"] !== "number") {
|
|
47866
|
+
fetchPercentileClips("slow-cycles");
|
|
47867
|
+
}
|
|
47868
|
+
}
|
|
47869
|
+
}, [
|
|
47870
|
+
activeInitialTimeFilter,
|
|
47871
|
+
categoriesForTree,
|
|
47872
|
+
endTime,
|
|
47873
|
+
fetchClipMetadata,
|
|
47874
|
+
fetchPercentileClips,
|
|
47875
|
+
isCategoryExternallyManaged,
|
|
47876
|
+
isTimeFilterActive,
|
|
47877
|
+
percentileCounts,
|
|
47878
|
+
scopedCategoryTotals,
|
|
47879
|
+
shouldShowCategory,
|
|
47880
|
+
showPercentileCycleFilters,
|
|
47881
|
+
startTime
|
|
47882
|
+
]);
|
|
46848
47883
|
const percentileCountsKey = React125.useMemo(() => {
|
|
46849
47884
|
if (!workspaceId || !date || shift === void 0) {
|
|
46850
47885
|
return null;
|
|
@@ -46867,12 +47902,15 @@ var FileManagerFilters = ({
|
|
|
46867
47902
|
}
|
|
46868
47903
|
percentileCountsKeyRef.current = percentileCountsKey;
|
|
46869
47904
|
percentilePrefetchRef.current = { key: null, types: /* @__PURE__ */ new Set() };
|
|
47905
|
+
if (prefetchedPercentileClips && (Array.isArray(prefetchedPercentileClips["fast-cycles"]) || Array.isArray(prefetchedPercentileClips["slow-cycles"]))) {
|
|
47906
|
+
return;
|
|
47907
|
+
}
|
|
46870
47908
|
setPercentileCounts({
|
|
46871
47909
|
"fast-cycles": null,
|
|
46872
47910
|
"slow-cycles": null
|
|
46873
47911
|
});
|
|
46874
47912
|
setPercentileClips({});
|
|
46875
|
-
}, [showPercentileCycleFilters, percentileCountsKey]);
|
|
47913
|
+
}, [prefetchedPercentileClips, showPercentileCycleFilters, percentileCountsKey]);
|
|
46876
47914
|
React125.useEffect(() => {
|
|
46877
47915
|
if (!showPercentileCycleFilters) {
|
|
46878
47916
|
return;
|
|
@@ -46928,11 +47966,20 @@ var FileManagerFilters = ({
|
|
|
46928
47966
|
const data = await response.json();
|
|
46929
47967
|
const fastCycles = data?.counts?.["fast-cycles"];
|
|
46930
47968
|
const slowCycles = data?.counts?.["slow-cycles"];
|
|
46931
|
-
|
|
46932
|
-
|
|
46933
|
-
|
|
46934
|
-
|
|
46935
|
-
|
|
47969
|
+
if (typeof fastCycles === "number" || typeof slowCycles === "number") {
|
|
47970
|
+
setPercentileCounts((prev) => {
|
|
47971
|
+
const nextFastCycles = typeof fastCycles === "number" ? fastCycles : prev["fast-cycles"];
|
|
47972
|
+
const nextSlowCycles = typeof slowCycles === "number" ? slowCycles : prev["slow-cycles"];
|
|
47973
|
+
if (prev["fast-cycles"] === nextFastCycles && prev["slow-cycles"] === nextSlowCycles) {
|
|
47974
|
+
return prev;
|
|
47975
|
+
}
|
|
47976
|
+
return {
|
|
47977
|
+
...prev,
|
|
47978
|
+
"fast-cycles": nextFastCycles,
|
|
47979
|
+
"slow-cycles": nextSlowCycles
|
|
47980
|
+
};
|
|
47981
|
+
});
|
|
47982
|
+
}
|
|
46936
47983
|
if (options?.prefetchClips) {
|
|
46937
47984
|
if (percentilePrefetchRef.current.key !== requestKey) {
|
|
46938
47985
|
percentilePrefetchRef.current = { key: requestKey, types: /* @__PURE__ */ new Set() };
|
|
@@ -46951,37 +47998,30 @@ var FileManagerFilters = ({
|
|
|
46951
47998
|
}
|
|
46952
47999
|
}, [workspaceId, date, shift, filterState.percentile, showPercentileCycleFilters, supabase, percentileCounts, percentileClips, fetchPercentileClips]);
|
|
46953
48000
|
React125.useEffect(() => {
|
|
46954
|
-
if (!showPercentileCycleFilters || !isReady || !percentileCountsKey) {
|
|
48001
|
+
if (!showPercentileCycleFilters || !isReady || !percentileCountsKey || isTimeFilterActive) {
|
|
46955
48002
|
return;
|
|
46956
48003
|
}
|
|
46957
|
-
|
|
46958
|
-
|
|
46959
|
-
|
|
46960
|
-
if (
|
|
46961
|
-
|
|
46962
|
-
} else {
|
|
46963
|
-
setTimeout(schedule, 0);
|
|
46964
|
-
}
|
|
46965
|
-
}, [showPercentileCycleFilters, isReady, percentileCountsKey, fetchPercentileCounts]);
|
|
46966
|
-
const shouldShowCategory = React125.useCallback((categoryId) => {
|
|
46967
|
-
switch (categoryId) {
|
|
46968
|
-
case "fast-cycles":
|
|
46969
|
-
return showPercentileCycleFilters && filterState.showFastCycles;
|
|
46970
|
-
case "slow-cycles":
|
|
46971
|
-
return showPercentileCycleFilters && filterState.showSlowCycles;
|
|
46972
|
-
case "longest-idles":
|
|
46973
|
-
return false;
|
|
46974
|
-
// filterState.showLongestIdles; // Temporarily disabled
|
|
46975
|
-
case "cycle_completion":
|
|
46976
|
-
return filterState.showCycleCompletion;
|
|
46977
|
-
case "idle_time":
|
|
46978
|
-
return filterState.showIdleTime;
|
|
46979
|
-
case "sop_deviations":
|
|
46980
|
-
return filterState.showSopDeviations;
|
|
46981
|
-
default:
|
|
46982
|
-
return true;
|
|
48004
|
+
fetchPercentileCounts({ prefetchClips: true });
|
|
48005
|
+
}, [showPercentileCycleFilters, isReady, percentileCountsKey, fetchPercentileCounts, isTimeFilterActive]);
|
|
48006
|
+
React125.useEffect(() => {
|
|
48007
|
+
if (!isReady || isTimeFilterActive || activeFilter !== RECENT_FLOW_RED_STREAK_CLIP_TYPE2) {
|
|
48008
|
+
return;
|
|
46983
48009
|
}
|
|
46984
|
-
|
|
48010
|
+
["cycle_completion", "idle_time"].forEach((categoryId) => {
|
|
48011
|
+
if ((counts?.[categoryId] || 0) <= 0 || hasKnownClipMetadata(categoryId) || isCategoryExternallyManaged(categoryId)) {
|
|
48012
|
+
return;
|
|
48013
|
+
}
|
|
48014
|
+
fetchClipMetadata(categoryId, 1);
|
|
48015
|
+
});
|
|
48016
|
+
}, [
|
|
48017
|
+
activeFilter,
|
|
48018
|
+
counts,
|
|
48019
|
+
fetchClipMetadata,
|
|
48020
|
+
hasKnownClipMetadata,
|
|
48021
|
+
isCategoryExternallyManaged,
|
|
48022
|
+
isReady,
|
|
48023
|
+
isTimeFilterActive
|
|
48024
|
+
]);
|
|
46985
48025
|
const getPercentileIcon = React125.useCallback((type, isExpanded, colorClasses) => {
|
|
46986
48026
|
const iconMap = {
|
|
46987
48027
|
"fast-cycles": { icon: lucideReact.TrendingUp, color: "text-green-600" },
|
|
@@ -47010,12 +48050,29 @@ var FileManagerFilters = ({
|
|
|
47010
48050
|
newExpanded.add(activeFilter);
|
|
47011
48051
|
return newExpanded;
|
|
47012
48052
|
});
|
|
47013
|
-
const category =
|
|
47014
|
-
if (category
|
|
47015
|
-
|
|
48053
|
+
const category = categoriesForTree.find((cat) => cat.id === activeFilter);
|
|
48054
|
+
if (category) {
|
|
48055
|
+
if (isInitialTimeFilterCategory(activeFilter) && !isCategoryExternallyManaged(activeFilter) && !hasCompleteMetadataForInitialTimeFilter(activeFilter)) {
|
|
48056
|
+
fetchClipMetadata(activeFilter, 1);
|
|
48057
|
+
} else if (!isCategoryExternallyManaged(activeFilter) && !clipMetadataRef.current[activeFilter]) {
|
|
48058
|
+
fetchClipMetadata(activeFilter, 1);
|
|
48059
|
+
}
|
|
47016
48060
|
}
|
|
47017
48061
|
}
|
|
47018
48062
|
}, [activeFilter]);
|
|
48063
|
+
React125.useEffect(() => {
|
|
48064
|
+
const requestedCategory = activeFilter;
|
|
48065
|
+
if (!requestedCategory || !isInitialTimeFilterCategory(requestedCategory) || isCategoryExternallyManaged(requestedCategory) || hasCompleteMetadataForInitialTimeFilter(requestedCategory)) {
|
|
48066
|
+
return;
|
|
48067
|
+
}
|
|
48068
|
+
fetchClipMetadata(requestedCategory, 1);
|
|
48069
|
+
}, [
|
|
48070
|
+
activeFilter,
|
|
48071
|
+
fetchClipMetadata,
|
|
48072
|
+
hasCompleteMetadataForInitialTimeFilter,
|
|
48073
|
+
isCategoryExternallyManaged,
|
|
48074
|
+
isInitialTimeFilterCategory
|
|
48075
|
+
]);
|
|
47019
48076
|
React125.useEffect(() => {
|
|
47020
48077
|
const handleEscape = (e) => {
|
|
47021
48078
|
if (e.key === "Escape") {
|
|
@@ -47079,25 +48136,34 @@ var FileManagerFilters = ({
|
|
|
47079
48136
|
}
|
|
47080
48137
|
try {
|
|
47081
48138
|
const clipDate = new Date(clipTimestamp);
|
|
47082
|
-
const
|
|
48139
|
+
const clipParts = new Intl.DateTimeFormat("en-US", {
|
|
47083
48140
|
hour12: false,
|
|
47084
48141
|
hour: "2-digit",
|
|
47085
48142
|
minute: "2-digit",
|
|
47086
|
-
timeZone:
|
|
47087
|
-
});
|
|
47088
|
-
|
|
48143
|
+
timeZone: activeTimeFilterTimezone
|
|
48144
|
+
}).formatToParts(clipDate);
|
|
48145
|
+
const hourValue = clipParts.find((part) => part.type === "hour")?.value;
|
|
48146
|
+
const minuteValue = clipParts.find((part) => part.type === "minute")?.value;
|
|
48147
|
+
const clipMinute = timeValueToMinutes(`${hourValue}:${minuteValue}`);
|
|
48148
|
+
return clipMinute === null ? false : isMinuteInTimeWindow(clipMinute, startTime, endTime);
|
|
47089
48149
|
} catch (error) {
|
|
47090
48150
|
console.error("[FileManager] Error parsing clip timestamp:", error);
|
|
47091
48151
|
return true;
|
|
47092
48152
|
}
|
|
47093
|
-
}, [isTimeFilterActive, startTime, endTime,
|
|
48153
|
+
}, [isTimeFilterActive, startTime, endTime, activeTimeFilterTimezone]);
|
|
47094
48154
|
const filterTree = React125.useMemo(() => {
|
|
47095
48155
|
const tree = [];
|
|
47096
48156
|
const regularCategoryNodes = [];
|
|
47097
|
-
|
|
48157
|
+
categoriesForTree.forEach((category) => {
|
|
48158
|
+
if (category.id === "fast-cycles" || category.id === "slow-cycles") {
|
|
48159
|
+
return;
|
|
48160
|
+
}
|
|
47098
48161
|
const categoryCount = counts?.[category.id] || 0;
|
|
47099
|
-
const
|
|
47100
|
-
|
|
48162
|
+
const categoryMetadataClips = getAvailableClipMetadata(category.id);
|
|
48163
|
+
const categoryVideoFallbackClips = categoryMetadataClips.length === 0 && isInitialTimeFilterCategory(category.id) ? videos.filter((video) => video.type === category.id).map(buildClipMetadataFromVideo).filter((clip) => isClipInTimeRange(clip.clip_timestamp)) : [];
|
|
48164
|
+
const categoryClips = categoryMetadataClips.length > 0 ? categoryMetadataClips : categoryVideoFallbackClips;
|
|
48165
|
+
const timeFilteredClips = categoryClips.filter((clip) => isClipInTimeRange(clip.clip_timestamp));
|
|
48166
|
+
let filteredClips = timeFilteredClips;
|
|
47101
48167
|
if (category.id === RECENT_FLOW_RED_STREAK_CLIP_TYPE2) {
|
|
47102
48168
|
filteredClips = sortRedFlowMetadata(filteredClips);
|
|
47103
48169
|
}
|
|
@@ -47107,9 +48173,22 @@ var FileManagerFilters = ({
|
|
|
47107
48173
|
return classification?.label === idleLabelFilter;
|
|
47108
48174
|
});
|
|
47109
48175
|
}
|
|
47110
|
-
const
|
|
48176
|
+
const scopedTotal = typeof scopedCategoryTotals[category.id] === "number" ? scopedCategoryTotals[category.id] : null;
|
|
48177
|
+
const isScopedByTimeFilter = isCategoryScopedByTimeFilter(category.id);
|
|
48178
|
+
const scopedPageLoaded = isScopedByTimeFilter && typeof scopedCategoryTotals[category.id] === "number";
|
|
48179
|
+
const scopedResponseHadOutOfHourClips = categoryClips.length > filteredClips.length;
|
|
48180
|
+
const shouldTrustScopedTotal = !scopedResponseHadOutOfHourClips || Boolean(categoryHasMore[category.id]);
|
|
48181
|
+
const isCategoryMetadataLoading = Array.from(loadingCategories).some((key) => key.startsWith(`${category.id}-`));
|
|
48182
|
+
const isScopedTotalPending = isTimeFilterActive && isScopedByTimeFilter && scopedTotal === null;
|
|
48183
|
+
const displayCount = isScopedTotalPending ? null : isTimeFilterActive && isScopedByTimeFilter && scopedTotal !== null ? shouldTrustScopedTotal ? scopedTotal : filteredClips.length : isTimeFilterActive || category.id === "idle_time" && idleLabelFilter ? filteredClips.length : categoryCount;
|
|
48184
|
+
const shouldShowScopedEmptyCategory = Boolean(
|
|
48185
|
+
scopedPageLoaded && !isCategoryMetadataLoading && !categoryHasMore[category.id] && filteredClips.length === 0 && (isRequiredHourlyFilterCategory(category.id) || activeFilter === category.id)
|
|
48186
|
+
);
|
|
48187
|
+
const shouldShowScopedLoadingCategory = Boolean(
|
|
48188
|
+
isScopedTotalPending && (isRequiredHourlyFilterCategory(category.id) || !activeInitialTimeFilter || activeFilter === category.id)
|
|
48189
|
+
);
|
|
47111
48190
|
const shouldShowEmptyIdleTime = category.id === "idle_time" && idleLabelFilter;
|
|
47112
|
-
if ((displayCount > 0 || shouldShowEmptyIdleTime) && shouldShowCategory(category.id)) {
|
|
48191
|
+
if ((typeof displayCount === "number" && displayCount > 0 || shouldShowEmptyIdleTime || isCategoryMetadataLoading || shouldShowScopedEmptyCategory || shouldShowScopedLoadingCategory) && shouldShowCategory(category.id)) {
|
|
47113
48192
|
const colorClasses = getColorClasses(category.color);
|
|
47114
48193
|
const clipNodes = filteredClips.map((clip, index) => {
|
|
47115
48194
|
const cycleTime = extractCycleTimeSeconds(clip);
|
|
@@ -47119,7 +48198,7 @@ var FileManagerFilters = ({
|
|
|
47119
48198
|
const baseTimeLabel = formatClipExplorerTimeLabel({
|
|
47120
48199
|
categoryId: category.id,
|
|
47121
48200
|
clipTimestamp: clip.clip_timestamp,
|
|
47122
|
-
timezone,
|
|
48201
|
+
timezone: activeTimeFilterTimezone,
|
|
47123
48202
|
durationSeconds: idleDuration ?? clip.duration,
|
|
47124
48203
|
idleStartTime: clip.idle_start_time,
|
|
47125
48204
|
idleEndTime: clip.idle_end_time,
|
|
@@ -47157,6 +48236,7 @@ var FileManagerFilters = ({
|
|
|
47157
48236
|
type: "category",
|
|
47158
48237
|
count: displayCount,
|
|
47159
48238
|
// Use filtered count when time filter is active
|
|
48239
|
+
countLoading: isScopedTotalPending || isCategoryMetadataLoading && scopedTotal === null,
|
|
47160
48240
|
children: clipNodes,
|
|
47161
48241
|
// Use clip nodes from metadata
|
|
47162
48242
|
icon: expandedNodes.has(category.id) ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpen, { className: `h-4 w-4 ${colorClasses.text}` }) : getCategoryIcon(category.icon, colorClasses),
|
|
@@ -47164,10 +48244,18 @@ var FileManagerFilters = ({
|
|
|
47164
48244
|
});
|
|
47165
48245
|
}
|
|
47166
48246
|
});
|
|
47167
|
-
const filteredFastCycles = (
|
|
47168
|
-
|
|
48247
|
+
const filteredFastCycles = sortPercentileCycleClipsForDisplay(
|
|
48248
|
+
"fast-cycles",
|
|
48249
|
+
(percentileClips["fast-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""))
|
|
48250
|
+
);
|
|
48251
|
+
const filteredSlowCycles = sortPercentileCycleClipsForDisplay(
|
|
48252
|
+
"slow-cycles",
|
|
48253
|
+
(percentileClips["slow-cycles"] || []).filter((clip) => isClipInTimeRange(clip.creation_timestamp || ""))
|
|
48254
|
+
);
|
|
47169
48255
|
const fastCount = typeof percentileCounts["fast-cycles"] === "number" ? percentileCounts["fast-cycles"] : null;
|
|
47170
48256
|
const slowCount = typeof percentileCounts["slow-cycles"] === "number" ? percentileCounts["slow-cycles"] : null;
|
|
48257
|
+
const isFastCountPending = showPercentileCycleFilters && fastCount === null;
|
|
48258
|
+
const isSlowCountPending = showPercentileCycleFilters && slowCount === null;
|
|
47171
48259
|
const percentileCategories = showPercentileCycleFilters ? [
|
|
47172
48260
|
{
|
|
47173
48261
|
id: "fast-cycles",
|
|
@@ -47176,6 +48264,7 @@ var FileManagerFilters = ({
|
|
|
47176
48264
|
description: "Top 10% fastest performance",
|
|
47177
48265
|
type: "percentile-category",
|
|
47178
48266
|
count: isTimeFilterActive ? fastCount === null && filteredFastCycles.length === 0 ? null : filteredFastCycles.length : fastCount,
|
|
48267
|
+
countLoading: isFastCountPending,
|
|
47179
48268
|
icon: getPercentileIcon("fast-cycles", expandedNodes.has("fast-cycles"), { text: "text-green-600" }),
|
|
47180
48269
|
color: "green",
|
|
47181
48270
|
percentileType: "fast-cycles",
|
|
@@ -47187,7 +48276,7 @@ var FileManagerFilters = ({
|
|
|
47187
48276
|
label: `${formatClipExplorerTimeLabel({
|
|
47188
48277
|
categoryId: "fast-cycles",
|
|
47189
48278
|
clipTimestamp: clip.creation_timestamp || clip.timestamp || "",
|
|
47190
|
-
timezone,
|
|
48279
|
+
timezone: activeTimeFilterTimezone,
|
|
47191
48280
|
durationSeconds: cycleTime
|
|
47192
48281
|
})}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
|
|
47193
48282
|
type: "video",
|
|
@@ -47207,6 +48296,7 @@ var FileManagerFilters = ({
|
|
|
47207
48296
|
description: "Bottom 10% slowest performance",
|
|
47208
48297
|
type: "percentile-category",
|
|
47209
48298
|
count: isTimeFilterActive ? slowCount === null && filteredSlowCycles.length === 0 ? null : filteredSlowCycles.length : slowCount,
|
|
48299
|
+
countLoading: isSlowCountPending,
|
|
47210
48300
|
icon: getPercentileIcon("slow-cycles", expandedNodes.has("slow-cycles"), { text: "text-red-600" }),
|
|
47211
48301
|
color: "red",
|
|
47212
48302
|
percentileType: "slow-cycles",
|
|
@@ -47218,7 +48308,7 @@ var FileManagerFilters = ({
|
|
|
47218
48308
|
label: `${formatClipExplorerTimeLabel({
|
|
47219
48309
|
categoryId: "slow-cycles",
|
|
47220
48310
|
clipTimestamp: clip.creation_timestamp || clip.timestamp || "",
|
|
47221
|
-
timezone,
|
|
48311
|
+
timezone: activeTimeFilterTimezone,
|
|
47222
48312
|
durationSeconds: cycleTime
|
|
47223
48313
|
})}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
|
|
47224
48314
|
type: "video",
|
|
@@ -47249,7 +48339,7 @@ var FileManagerFilters = ({
|
|
|
47249
48339
|
hour12: true,
|
|
47250
48340
|
hour: 'numeric',
|
|
47251
48341
|
minute: '2-digit',
|
|
47252
|
-
timeZone:
|
|
48342
|
+
timeZone: activeTimeFilterTimezone
|
|
47253
48343
|
});
|
|
47254
48344
|
|
|
47255
48345
|
return {
|
|
@@ -47268,7 +48358,11 @@ var FileManagerFilters = ({
|
|
|
47268
48358
|
const orderedIds = [RECENT_FLOW_RED_STREAK_CLIP_TYPE2, "cycle_completion", "fast-cycles", "slow-cycles", "idle_time"];
|
|
47269
48359
|
orderedIds.forEach((orderedId) => {
|
|
47270
48360
|
const percentileCategory = percentileCategories.find((cat) => cat.id === orderedId);
|
|
47271
|
-
const shouldIncludePercentile = percentileCategory ? typeof percentileCategory.count === "number"
|
|
48361
|
+
const shouldIncludePercentile = percentileCategory ? typeof percentileCategory.count === "number" || Boolean(
|
|
48362
|
+
isTimeFilterActive && isRequiredHourlyFilterCategory(orderedId) && typeof percentileCategory.count === "number" && percentileCategory.count === 0
|
|
48363
|
+
) || Boolean(
|
|
48364
|
+
percentileCategory.countLoading && (isRequiredHourlyFilterCategory(orderedId) || !activeInitialTimeFilter || activeFilter === orderedId)
|
|
48365
|
+
) : false;
|
|
47272
48366
|
if (percentileCategory && shouldIncludePercentile && shouldShowCategory(orderedId)) {
|
|
47273
48367
|
tree.push(percentileCategory);
|
|
47274
48368
|
}
|
|
@@ -47283,23 +48377,263 @@ var FileManagerFilters = ({
|
|
|
47283
48377
|
}
|
|
47284
48378
|
});
|
|
47285
48379
|
percentileCategories.forEach((category) => {
|
|
47286
|
-
const shouldIncludePercentile = typeof category.count === "number"
|
|
48380
|
+
const shouldIncludePercentile = typeof category.count === "number" || Boolean(
|
|
48381
|
+
isTimeFilterActive && isRequiredHourlyFilterCategory(category.id) && typeof category.count === "number" && category.count === 0
|
|
48382
|
+
) || Boolean(
|
|
48383
|
+
category.countLoading && (isRequiredHourlyFilterCategory(category.id) || !activeInitialTimeFilter || activeFilter === category.id)
|
|
48384
|
+
);
|
|
47287
48385
|
if (!orderedIds.includes(category.id) && shouldIncludePercentile && shouldShowCategory(category.id)) {
|
|
47288
48386
|
tree.push(category);
|
|
47289
48387
|
}
|
|
47290
48388
|
});
|
|
47291
48389
|
return tree;
|
|
47292
|
-
}, [
|
|
48390
|
+
}, [categoriesForTree, expandedNodes, counts, getAvailableClipMetadata, percentileCounts, percentileClips, shouldShowCategory, getPercentileIcon, isClipInTimeRange, isTimeFilterActive, showPercentileCycleFilters, loadingCategories, activeTimeFilterTimezone, isInitialTimeFilterCategory, isRequiredHourlyFilterCategory, hasCompleteMetadataForInitialTimeFilter, scopedCategoryTotals, isCategoryScopedByTimeFilter, categoryHasMore, activeFilter]);
|
|
48391
|
+
const chartHandoffVideoFallbackTree = React125.useMemo(() => {
|
|
48392
|
+
if (!startTime || !endTime || !activeInitialTimeFilter || !activeFilter) {
|
|
48393
|
+
return [];
|
|
48394
|
+
}
|
|
48395
|
+
const fallbackCategory = categoriesForTree.find((category) => category.id === activeFilter) || categoriesForTree.find((category) => videos.some((video) => video.type === category.id));
|
|
48396
|
+
if (!fallbackCategory) {
|
|
48397
|
+
return [];
|
|
48398
|
+
}
|
|
48399
|
+
const fallbackVideos = videos.filter((video) => video.type === fallbackCategory.id);
|
|
48400
|
+
const sourceVideos = fallbackVideos.map(buildClipMetadataFromVideo).filter((clip) => isClipInTimeRange(clip.clip_timestamp));
|
|
48401
|
+
if (sourceVideos.length === 0) {
|
|
48402
|
+
return [];
|
|
48403
|
+
}
|
|
48404
|
+
const colorClasses = getColorClasses(fallbackCategory.color);
|
|
48405
|
+
const clipNodes = sourceVideos.map((clip, index) => {
|
|
48406
|
+
const cycleTime = extractCycleTimeSeconds(clip);
|
|
48407
|
+
const baseTimeLabel = formatClipExplorerTimeLabel({
|
|
48408
|
+
categoryId: fallbackCategory.id,
|
|
48409
|
+
clipTimestamp: clip.clip_timestamp,
|
|
48410
|
+
timezone: activeTimeFilterTimezone,
|
|
48411
|
+
durationSeconds: clip.duration,
|
|
48412
|
+
idleStartTime: clip.idle_start_time,
|
|
48413
|
+
idleEndTime: clip.idle_end_time,
|
|
48414
|
+
clipStartTime: clip.clip_start_time,
|
|
48415
|
+
clipEndTime: clip.clip_end_time
|
|
48416
|
+
});
|
|
48417
|
+
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)` : ""}`;
|
|
48418
|
+
return {
|
|
48419
|
+
id: clip.id,
|
|
48420
|
+
label: displayLabel,
|
|
48421
|
+
type: "video",
|
|
48422
|
+
icon: getSeverityIcon(clip.severity, fallbackCategory.id, cycleTime, resolvedTargetCycleTime, clip.clipId),
|
|
48423
|
+
timestamp: clip.clip_timestamp,
|
|
48424
|
+
severity: clip.severity,
|
|
48425
|
+
clipId: clip.clipId,
|
|
48426
|
+
categoryId: fallbackCategory.id,
|
|
48427
|
+
clipPosition: index + 1,
|
|
48428
|
+
cycleTimeSeconds: cycleTime,
|
|
48429
|
+
duration: clip.duration,
|
|
48430
|
+
cycleItemCount: null,
|
|
48431
|
+
redFlowSeverityScore: clip.red_flow_severity_score ?? null,
|
|
48432
|
+
redFlowExplanationSummary: clip.red_flow_explanation_summary ?? clip.red_flow_explanation?.summary ?? null
|
|
48433
|
+
};
|
|
48434
|
+
});
|
|
48435
|
+
return [{
|
|
48436
|
+
id: activeFilter,
|
|
48437
|
+
label: fallbackCategory.id === RECENT_FLOW_RED_STREAK_CLIP_TYPE2 ? RECENT_FLOW_RED_STREAK_DISPLAY_LABEL : fallbackCategory.label,
|
|
48438
|
+
subtitle: fallbackCategory.subtitle || fallbackCategory.description,
|
|
48439
|
+
description: fallbackCategory.description,
|
|
48440
|
+
type: "category",
|
|
48441
|
+
count: clipNodes.length,
|
|
48442
|
+
children: clipNodes,
|
|
48443
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpen, { className: `h-4 w-4 ${colorClasses.text}` }),
|
|
48444
|
+
color: fallbackCategory.color
|
|
48445
|
+
}];
|
|
48446
|
+
}, [
|
|
48447
|
+
activeFilter,
|
|
48448
|
+
activeInitialTimeFilter,
|
|
48449
|
+
activeTimeFilterTimezone,
|
|
48450
|
+
categoriesForTree,
|
|
48451
|
+
counts,
|
|
48452
|
+
currentVideoId,
|
|
48453
|
+
endTime,
|
|
48454
|
+
getDisplayValue,
|
|
48455
|
+
isClipInTimeRange,
|
|
48456
|
+
resolvedTargetCycleTime,
|
|
48457
|
+
startTime,
|
|
48458
|
+
videos
|
|
48459
|
+
]);
|
|
48460
|
+
const displayedFilterTree = React125.useMemo(() => {
|
|
48461
|
+
if (chartHandoffVideoFallbackTree.length === 0) {
|
|
48462
|
+
return filterTree;
|
|
48463
|
+
}
|
|
48464
|
+
if (filterTree.length === 0) {
|
|
48465
|
+
return chartHandoffVideoFallbackTree;
|
|
48466
|
+
}
|
|
48467
|
+
const fallbackNode = chartHandoffVideoFallbackTree[0];
|
|
48468
|
+
const activeNode = filterTree.find((node) => node.id === activeFilter);
|
|
48469
|
+
const activeNodeHasChildren = (activeNode?.children?.length || 0) > 0;
|
|
48470
|
+
const shouldReplaceActiveNode = Boolean(
|
|
48471
|
+
activeFilter && fallbackNode && activeNode && !activeNodeHasChildren && isInitialTimeFilterCategory(activeFilter)
|
|
48472
|
+
);
|
|
48473
|
+
if (!shouldReplaceActiveNode) {
|
|
48474
|
+
return filterTree;
|
|
48475
|
+
}
|
|
48476
|
+
return filterTree.map((node) => node.id === activeFilter ? fallbackNode : node);
|
|
48477
|
+
}, [activeFilter, chartHandoffVideoFallbackTree, filterTree, isInitialTimeFilterCategory]);
|
|
48478
|
+
const getSelectionContextForNodes = React125.useCallback((categoryId, clipNodes) => {
|
|
48479
|
+
const normalizeSeverity = (severity) => severity === "low" || severity === "medium" || severity === "high" ? severity : "medium";
|
|
48480
|
+
const scopedNodes = clipNodes.filter((node) => node.type === "video" && Boolean(node.clipId || node.id));
|
|
48481
|
+
const scopedNodeClips = scopedNodes.map((node, index) => ({
|
|
48482
|
+
id: node.clipId || node.id,
|
|
48483
|
+
clipId: node.clipId || node.id,
|
|
48484
|
+
clip_timestamp: node.timestamp || "",
|
|
48485
|
+
creation_timestamp: node.timestamp || "",
|
|
48486
|
+
description: node.label,
|
|
48487
|
+
severity: normalizeSeverity(node.severity),
|
|
48488
|
+
category: categoryId,
|
|
48489
|
+
duration: node.duration,
|
|
48490
|
+
cycle_time_seconds: node.cycleTimeSeconds,
|
|
48491
|
+
index
|
|
48492
|
+
}));
|
|
48493
|
+
const categoryClips = getAvailableClipMetadata(categoryId);
|
|
48494
|
+
const contextClips = isCategoryScopedByTimeFilter(categoryId) && scopedNodeClips.length > 0 ? scopedNodeClips : categoryClips.length > 0 ? categoryClips : scopedNodeClips;
|
|
48495
|
+
const scopedTotal = scopedCategoryTotals[categoryId];
|
|
48496
|
+
const total = typeof scopedTotal === "number" ? scopedTotal : counts?.[categoryId];
|
|
48497
|
+
return contextClips.length ? { clips: contextClips, total } : void 0;
|
|
48498
|
+
}, [counts, getAvailableClipMetadata, isCategoryScopedByTimeFilter, scopedCategoryTotals]);
|
|
48499
|
+
React125.useEffect(() => {
|
|
48500
|
+
if (!activeInitialTimeFilter || !startTime || !endTime || currentVideoId || !onClipSelect) {
|
|
48501
|
+
return;
|
|
48502
|
+
}
|
|
48503
|
+
const categoryPriority = Array.from(new Set([
|
|
48504
|
+
activeInitialTimeFilter.categoryId,
|
|
48505
|
+
activeFilter,
|
|
48506
|
+
...activeInitialTimeFilter.categoryIds || []
|
|
48507
|
+
].filter((value) => typeof value === "string" && value.length > 0)));
|
|
48508
|
+
const preferredCategoryId = categoryPriority[0];
|
|
48509
|
+
const orderedCategoryNodes = displayedFilterTree.filter((categoryNode) => isInitialTimeFilterCategory(categoryNode.id)).sort((left, right) => {
|
|
48510
|
+
const leftIndex = categoryPriority.indexOf(left.id);
|
|
48511
|
+
const rightIndex = categoryPriority.indexOf(right.id);
|
|
48512
|
+
const leftPriority = leftIndex === -1 ? Number.MAX_SAFE_INTEGER : leftIndex;
|
|
48513
|
+
const rightPriority = rightIndex === -1 ? Number.MAX_SAFE_INTEGER : rightIndex;
|
|
48514
|
+
return leftPriority - rightPriority;
|
|
48515
|
+
});
|
|
48516
|
+
const preferredCategoryNode = preferredCategoryId ? orderedCategoryNodes.find((categoryNode) => categoryNode.id === preferredCategoryId) : void 0;
|
|
48517
|
+
if (preferredCategoryId && !preferredCategoryNode) {
|
|
48518
|
+
const preferredKnownTotal = preferredCategoryId === "fast-cycles" || preferredCategoryId === "slow-cycles" ? percentileCounts[preferredCategoryId] : scopedCategoryTotals[preferredCategoryId];
|
|
48519
|
+
if (preferredKnownTotal !== 0) {
|
|
48520
|
+
return;
|
|
48521
|
+
}
|
|
48522
|
+
}
|
|
48523
|
+
const preferredFirstClipNode = preferredCategoryNode?.children?.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
|
|
48524
|
+
if (preferredCategoryId && preferredCategoryNode && !preferredFirstClipNode) {
|
|
48525
|
+
const preferredKnownTotal = preferredCategoryId === "fast-cycles" || preferredCategoryId === "slow-cycles" ? percentileCounts[preferredCategoryId] : scopedCategoryTotals[preferredCategoryId];
|
|
48526
|
+
if (preferredKnownTotal !== 0) {
|
|
48527
|
+
return;
|
|
48528
|
+
}
|
|
48529
|
+
}
|
|
48530
|
+
for (const categoryNode of orderedCategoryNodes) {
|
|
48531
|
+
const firstClipNode = categoryNode.children?.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
|
|
48532
|
+
if (!firstClipNode?.clipId || !firstClipNode.categoryId) {
|
|
48533
|
+
continue;
|
|
48534
|
+
}
|
|
48535
|
+
const selectionKey = `${activeTimeFilterKey}:${firstClipNode.categoryId}:${firstClipNode.clipId}:${currentVideoId || "none"}`;
|
|
48536
|
+
if (autoSelectedScopedClipRef.current === selectionKey) {
|
|
48537
|
+
return;
|
|
48538
|
+
}
|
|
48539
|
+
autoSelectedScopedClipRef.current = selectionKey;
|
|
48540
|
+
const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, categoryNode.children || []);
|
|
48541
|
+
onClipSelect(
|
|
48542
|
+
firstClipNode.categoryId,
|
|
48543
|
+
firstClipNode.clipId,
|
|
48544
|
+
firstClipNode.clipPosition,
|
|
48545
|
+
selectionContext
|
|
48546
|
+
);
|
|
48547
|
+
return;
|
|
48548
|
+
}
|
|
48549
|
+
}, [
|
|
48550
|
+
activeInitialTimeFilter,
|
|
48551
|
+
activeFilter,
|
|
48552
|
+
activeTimeFilterKey,
|
|
48553
|
+
currentVideoId,
|
|
48554
|
+
displayedFilterTree,
|
|
48555
|
+
endTime,
|
|
48556
|
+
getSelectionContextForNodes,
|
|
48557
|
+
isInitialTimeFilterCategory,
|
|
48558
|
+
onClipSelect,
|
|
48559
|
+
percentileCounts,
|
|
48560
|
+
scopedCategoryTotals,
|
|
48561
|
+
startTime
|
|
48562
|
+
]);
|
|
48563
|
+
const isChartHandoffLoading = React125.useMemo(() => {
|
|
48564
|
+
if (!startTime || !endTime || !activeInitialTimeFilter || !activeFilter || !isInitialTimeFilterCategory(activeFilter)) {
|
|
48565
|
+
return false;
|
|
48566
|
+
}
|
|
48567
|
+
const pageOneLoadingKey = getMetadataLoadingKey(activeFilter, 1);
|
|
48568
|
+
return Boolean(
|
|
48569
|
+
activeCategoryLoading || Array.from(loadingCategories).some((key) => key === pageOneLoadingKey || key.startsWith(`${pageOneLoadingKey}-all`))
|
|
48570
|
+
);
|
|
48571
|
+
}, [
|
|
48572
|
+
activeCategoryLoading,
|
|
48573
|
+
activeFilter,
|
|
48574
|
+
activeInitialTimeFilter,
|
|
48575
|
+
endTime,
|
|
48576
|
+
getMetadataLoadingKey,
|
|
48577
|
+
isInitialTimeFilterCategory,
|
|
48578
|
+
loadingCategories,
|
|
48579
|
+
startTime
|
|
48580
|
+
]);
|
|
48581
|
+
React125.useEffect(() => {
|
|
48582
|
+
if (!isTimeFilterActive || !startTime || !endTime || !activeFilter || activeFilter === "all" || !onClipSelect) {
|
|
48583
|
+
return;
|
|
48584
|
+
}
|
|
48585
|
+
const activeCategoryNode = displayedFilterTree.find((categoryNode) => categoryNode.id === activeFilter);
|
|
48586
|
+
const activeClipNodes = (activeCategoryNode?.children || []).filter((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
|
|
48587
|
+
if (activeClipNodes.length === 0) {
|
|
48588
|
+
return;
|
|
48589
|
+
}
|
|
48590
|
+
const currentClipIsVisible = Boolean(
|
|
48591
|
+
currentVideoId && activeClipNodes.some((child) => (child.clipId || child.id) === currentVideoId)
|
|
48592
|
+
);
|
|
48593
|
+
if (currentClipIsVisible) {
|
|
48594
|
+
return;
|
|
48595
|
+
}
|
|
48596
|
+
const firstClipNode = activeClipNodes[0];
|
|
48597
|
+
if (!firstClipNode?.clipId || !firstClipNode.categoryId) {
|
|
48598
|
+
return;
|
|
48599
|
+
}
|
|
48600
|
+
const selectionKey = `${activeTimeFilterKey}:${firstClipNode.categoryId}:${firstClipNode.clipId}:${currentVideoId || "none"}`;
|
|
48601
|
+
if (autoSelectedScopedClipRef.current === selectionKey) {
|
|
48602
|
+
return;
|
|
48603
|
+
}
|
|
48604
|
+
autoSelectedScopedClipRef.current = selectionKey;
|
|
48605
|
+
const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, activeClipNodes);
|
|
48606
|
+
onClipSelect(
|
|
48607
|
+
firstClipNode.categoryId,
|
|
48608
|
+
firstClipNode.clipId,
|
|
48609
|
+
firstClipNode.clipPosition,
|
|
48610
|
+
selectionContext
|
|
48611
|
+
);
|
|
48612
|
+
}, [
|
|
48613
|
+
activeFilter,
|
|
48614
|
+
activeTimeFilterKey,
|
|
48615
|
+
currentVideoId,
|
|
48616
|
+
displayedFilterTree,
|
|
48617
|
+
endTime,
|
|
48618
|
+
getSelectionContextForNodes,
|
|
48619
|
+
isTimeFilterActive,
|
|
48620
|
+
onClipSelect,
|
|
48621
|
+
startTime
|
|
48622
|
+
]);
|
|
47293
48623
|
const toggleExpanded = (nodeId) => {
|
|
47294
48624
|
const newExpanded = new Set(expandedNodes);
|
|
47295
48625
|
if (newExpanded.has(nodeId)) {
|
|
47296
48626
|
newExpanded.delete(nodeId);
|
|
47297
48627
|
} else {
|
|
47298
48628
|
newExpanded.add(nodeId);
|
|
47299
|
-
const category =
|
|
47300
|
-
if (category
|
|
48629
|
+
const category = categoriesForTree.find((cat) => cat.id === nodeId);
|
|
48630
|
+
if (category) {
|
|
47301
48631
|
console.log(`[FileManager] Fetching clips for expanded category: ${nodeId}`);
|
|
47302
|
-
|
|
48632
|
+
if (isInitialTimeFilterCategory(nodeId) && !hasCompleteMetadataForInitialTimeFilter(nodeId)) {
|
|
48633
|
+
fetchClipMetadata(nodeId, 1);
|
|
48634
|
+
} else if (!hasKnownClipMetadata(nodeId) && !isCategoryExternallyManaged(nodeId)) {
|
|
48635
|
+
fetchClipMetadata(nodeId, 1);
|
|
48636
|
+
}
|
|
47303
48637
|
}
|
|
47304
48638
|
if (!isCategoryExternallyManaged(nodeId) && showPercentileCycleFilters && nodeId === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
|
|
47305
48639
|
fetchPercentileClips("fast-cycles");
|
|
@@ -47317,10 +48651,14 @@ var FileManagerFilters = ({
|
|
|
47317
48651
|
newExpanded.delete(node.id);
|
|
47318
48652
|
} else {
|
|
47319
48653
|
newExpanded.add(node.id);
|
|
47320
|
-
const category =
|
|
47321
|
-
if (category
|
|
48654
|
+
const category = categoriesForTree.find((cat) => cat.id === node.id);
|
|
48655
|
+
if (category) {
|
|
47322
48656
|
console.log(`[FileManager] Fetching clips for expanded category: ${node.id}`);
|
|
47323
|
-
|
|
48657
|
+
if (isInitialTimeFilterCategory(node.id) && !hasCompleteMetadataForInitialTimeFilter(node.id)) {
|
|
48658
|
+
fetchClipMetadata(node.id, 1);
|
|
48659
|
+
} else if (!hasKnownClipMetadata(node.id) && !isCategoryExternallyManaged(node.id)) {
|
|
48660
|
+
fetchClipMetadata(node.id, 1);
|
|
48661
|
+
}
|
|
47324
48662
|
}
|
|
47325
48663
|
if (!isCategoryExternallyManaged(node.id) && showPercentileCycleFilters && node.id === "fast-cycles" && (percentileClips["fast-cycles"] || []).length === 0) {
|
|
47326
48664
|
fetchPercentileClips("fast-cycles");
|
|
@@ -47330,7 +48668,21 @@ var FileManagerFilters = ({
|
|
|
47330
48668
|
}
|
|
47331
48669
|
}
|
|
47332
48670
|
setExpandedNodes(newExpanded);
|
|
47333
|
-
|
|
48671
|
+
if (node.id !== activeFilter) {
|
|
48672
|
+
onFilterChange(node.id);
|
|
48673
|
+
}
|
|
48674
|
+
if (isCategoryScopedByTimeFilter(node.id) && onClipSelect && node.children?.length) {
|
|
48675
|
+
const firstClipNode = node.children.find((child) => child.type === "video" && Boolean(child.clipId) && Boolean(child.categoryId));
|
|
48676
|
+
if (firstClipNode?.clipId && firstClipNode.categoryId) {
|
|
48677
|
+
const selectionContext = getSelectionContextForNodes(firstClipNode.categoryId, node.children);
|
|
48678
|
+
onClipSelect(
|
|
48679
|
+
firstClipNode.categoryId,
|
|
48680
|
+
firstClipNode.clipId,
|
|
48681
|
+
firstClipNode.clipPosition,
|
|
48682
|
+
selectionContext
|
|
48683
|
+
);
|
|
48684
|
+
}
|
|
48685
|
+
}
|
|
47334
48686
|
if (node.id === "fast-cycles") {
|
|
47335
48687
|
trackCoreEvent("Fast Clips Clicked", {
|
|
47336
48688
|
workspaceId,
|
|
@@ -47368,8 +48720,11 @@ var FileManagerFilters = ({
|
|
|
47368
48720
|
} else if (node.type === "video") {
|
|
47369
48721
|
if (onClipSelect && node.categoryId !== void 0 && node.clipId !== void 0) {
|
|
47370
48722
|
console.log(`[FileManager] Selecting clip: category=${node.categoryId}, clipId=${node.clipId}, position=${node.clipPosition}`);
|
|
47371
|
-
const
|
|
47372
|
-
const selectionContext =
|
|
48723
|
+
const categoryNode = displayedFilterTree.find((candidate) => candidate.id === node.categoryId);
|
|
48724
|
+
const selectionContext = getSelectionContextForNodes(
|
|
48725
|
+
node.categoryId,
|
|
48726
|
+
categoryNode?.children || [node]
|
|
48727
|
+
);
|
|
47373
48728
|
onClipSelect(node.categoryId, node.clipId, node.clipPosition, selectionContext);
|
|
47374
48729
|
} else {
|
|
47375
48730
|
const videoIndex = videos.findIndex((v) => v.id === node.id);
|
|
@@ -47382,24 +48737,40 @@ var FileManagerFilters = ({
|
|
|
47382
48737
|
const renderNode = (node, depth = 0) => {
|
|
47383
48738
|
const isExpanded = expandedNodes.has(node.id);
|
|
47384
48739
|
const isActive = activeFilter === node.id;
|
|
47385
|
-
const
|
|
47386
|
-
const
|
|
47387
|
-
|
|
47388
|
-
|
|
48740
|
+
const nodeClipId = node.clipId || node.id;
|
|
48741
|
+
const isCurrentVideo = Boolean(
|
|
48742
|
+
node.type === "video" && currentVideoId && currentVideoId === nodeClipId
|
|
48743
|
+
);
|
|
48744
|
+
const isCountUnknown = Boolean(
|
|
48745
|
+
(node.type === "category" || node.type === "percentile-category") && (node.count === null || node.countLoading)
|
|
48746
|
+
);
|
|
48747
|
+
const hasRenderedChildren = (node.children?.length || 0) > 0;
|
|
47389
48748
|
const loadedPage = categoryPages[node.id] || 0;
|
|
47390
48749
|
const pageOneLoadingKey = getMetadataLoadingKey(node.id, 1);
|
|
47391
48750
|
const nextMetadataPage = loadedPage + 1;
|
|
47392
48751
|
const nextLoadMorePage = (categoryPages[node.id] || 1) + 1;
|
|
47393
48752
|
const isPageOneLoading = loadingCategories.has(pageOneLoadingKey);
|
|
48753
|
+
const isFullCategoryLoading = Array.from(loadingCategories).some((key) => key.startsWith(`${pageOneLoadingKey}-all`));
|
|
47394
48754
|
const isLoadMoreLoading = loadedPage > 0 && loadingCategories.has(getMetadataLoadingKey(node.id, nextMetadataPage));
|
|
48755
|
+
const isNodeLoading = Boolean(
|
|
48756
|
+
isPageOneLoading || isFullCategoryLoading || activeCategoryLoading && node.id === activeFilter
|
|
48757
|
+
);
|
|
48758
|
+
const totalForLoadMore = isCategoryScopedByTimeFilter(node.id) && typeof scopedCategoryTotals[node.id] === "number" ? scopedCategoryTotals[node.id] : counts?.[node.id] || 0;
|
|
47395
48759
|
const showInitialLoadingState = Boolean(
|
|
47396
|
-
isExpanded && !
|
|
48760
|
+
isExpanded && !hasRenderedChildren && (node.type === "category" || node.type === "percentile-category") && isNodeLoading
|
|
48761
|
+
);
|
|
48762
|
+
const showScopedEmptyState = Boolean(
|
|
48763
|
+
isExpanded && !hasRenderedChildren && !isNodeLoading && !categoryHasMore[node.id] && isCategoryScopedByTimeFilter(node.id) && (node.type === "category" || node.type === "percentile-category")
|
|
47397
48764
|
);
|
|
48765
|
+
const hasChildren = isCountUnknown || (node.count || 0) > 0 || isNodeLoading || showScopedEmptyState;
|
|
47398
48766
|
const colorClasses = node.color ? getColorClasses(node.color) : null;
|
|
47399
48767
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "select-none animate-in", children: [
|
|
47400
48768
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
47401
48769
|
"div",
|
|
47402
48770
|
{
|
|
48771
|
+
"aria-current": isCurrentVideo ? "true" : void 0,
|
|
48772
|
+
"data-qa-clips-row-clip-id": node.type === "video" ? nodeClipId : void 0,
|
|
48773
|
+
"data-qa-clips-row-category-id": node.type === "video" ? node.categoryId : void 0,
|
|
47403
48774
|
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" : ""}`,
|
|
47404
48775
|
onClick: () => handleNodeClick(node),
|
|
47405
48776
|
children: [
|
|
@@ -47435,7 +48806,7 @@ var FileManagerFilters = ({
|
|
|
47435
48806
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between", children: [
|
|
47436
48807
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
47437
48808
|
/* @__PURE__ */ jsxRuntime.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 }),
|
|
47438
|
-
node.type === "category" && (node.subtitle ||
|
|
48809
|
+
node.type === "category" && (node.subtitle || categoriesForTree.find((c) => c.id === node.id)?.description) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle || categoriesForTree.find((c) => c.id === node.id)?.description }),
|
|
47439
48810
|
node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
|
|
47440
48811
|
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__ */ jsxRuntime.jsx("div", { className: "text-xs mt-0.5 font-medium", children: node.categoryId === "idle_time" ? (
|
|
47441
48812
|
// Show root cause label for idle time clips (text only, icon is on the left)
|
|
@@ -47471,7 +48842,7 @@ var FileManagerFilters = ({
|
|
|
47471
48842
|
})()
|
|
47472
48843
|
) })
|
|
47473
48844
|
] }),
|
|
47474
|
-
node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsxRuntime.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:
|
|
48845
|
+
node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : node.count }) })
|
|
47475
48846
|
] })
|
|
47476
48847
|
]
|
|
47477
48848
|
}
|
|
@@ -47482,6 +48853,7 @@ var FileManagerFilters = ({
|
|
|
47482
48853
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }),
|
|
47483
48854
|
"Loading clips..."
|
|
47484
48855
|
] }) }),
|
|
48856
|
+
showScopedEmptyState && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-2 px-3 text-center text-sm text-slate-500", children: "No clips found" }),
|
|
47485
48857
|
isLoadMoreLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center text-sm text-slate-500", children: [
|
|
47486
48858
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin mr-2 h-4 w-4 border-2 border-slate-300 border-t-blue-500 rounded-full" }),
|
|
47487
48859
|
"Loading more clips..."
|
|
@@ -47496,7 +48868,7 @@ var FileManagerFilters = ({
|
|
|
47496
48868
|
className: "w-full py-2 px-3 text-sm text-blue-600 hover:bg-blue-50 rounded-lg transition-colors duration-200 text-center",
|
|
47497
48869
|
children: [
|
|
47498
48870
|
"Load more clips (",
|
|
47499
|
-
(
|
|
48871
|
+
Math.max(0, totalForLoadMore - getAvailableClipMetadata(node.id).length),
|
|
47500
48872
|
" remaining)"
|
|
47501
48873
|
]
|
|
47502
48874
|
}
|
|
@@ -47559,6 +48931,8 @@ var FileManagerFilters = ({
|
|
|
47559
48931
|
e.stopPropagation();
|
|
47560
48932
|
setStartTime("");
|
|
47561
48933
|
setEndTime("");
|
|
48934
|
+
setActiveInitialTimeFilter(null);
|
|
48935
|
+
setScopedCategoryTotals({});
|
|
47562
48936
|
setIsTimeFilterActive(false);
|
|
47563
48937
|
},
|
|
47564
48938
|
className: "rounded-full p-0.5 transition-colors hover:bg-blue-100",
|
|
@@ -47692,6 +49066,8 @@ var FileManagerFilters = ({
|
|
|
47692
49066
|
onClick: () => {
|
|
47693
49067
|
setStartTime("");
|
|
47694
49068
|
setEndTime("");
|
|
49069
|
+
setActiveInitialTimeFilter(null);
|
|
49070
|
+
setScopedCategoryTotals({});
|
|
47695
49071
|
setStartSearchTerm("");
|
|
47696
49072
|
setEndSearchTerm("");
|
|
47697
49073
|
setIsTimeFilterActive(false);
|
|
@@ -47780,8 +49156,13 @@ var FileManagerFilters = ({
|
|
|
47780
49156
|
)
|
|
47781
49157
|
] }),
|
|
47782
49158
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 flex-1 min-h-0 overflow-y-auto scrollbar-thin", children: [
|
|
47783
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children:
|
|
47784
|
-
|
|
49159
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: displayedFilterTree.map((node) => renderNode(node)) }),
|
|
49160
|
+
displayedFilterTree.length === 0 && isChartHandoffLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
|
|
49161
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-8 w-8 animate-spin text-blue-500" }) }),
|
|
49162
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "Loading clips..." }),
|
|
49163
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500", children: "Finding clips from the selected hour" })
|
|
49164
|
+
] }),
|
|
49165
|
+
displayedFilterTree.length === 0 && !isChartHandoffLoading && (startTime || endTime) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
|
|
47785
49166
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-12 w-12 mx-auto" }) }),
|
|
47786
49167
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips found" }),
|
|
47787
49168
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500 mb-4", children: "No clips match the selected time range" }),
|
|
@@ -47791,6 +49172,8 @@ var FileManagerFilters = ({
|
|
|
47791
49172
|
onClick: () => {
|
|
47792
49173
|
setStartTime("");
|
|
47793
49174
|
setEndTime("");
|
|
49175
|
+
setActiveInitialTimeFilter(null);
|
|
49176
|
+
setScopedCategoryTotals({});
|
|
47794
49177
|
setIsTimeFilterActive(false);
|
|
47795
49178
|
},
|
|
47796
49179
|
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",
|
|
@@ -47798,12 +49181,12 @@ var FileManagerFilters = ({
|
|
|
47798
49181
|
}
|
|
47799
49182
|
)
|
|
47800
49183
|
] }),
|
|
47801
|
-
|
|
49184
|
+
displayedFilterTree.length === 0 && !startTime && !endTime && categories.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
|
|
47802
49185
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.HelpCircle, { className: "h-12 w-12 mx-auto" }) }),
|
|
47803
49186
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clip types available" }),
|
|
47804
49187
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500", children: "Loading clip categories..." })
|
|
47805
49188
|
] }),
|
|
47806
|
-
|
|
49189
|
+
displayedFilterTree.length === 0 && !startTime && !endTime && categories.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-12", children: [
|
|
47807
49190
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "h-12 w-12 mx-auto" }) }),
|
|
47808
49191
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-slate-700 mb-2", children: "No clips available" }),
|
|
47809
49192
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-slate-500", children: "No clips found for the selected time period" })
|
|
@@ -48210,6 +49593,65 @@ function useClipsRealtimeUpdates({
|
|
|
48210
49593
|
}
|
|
48211
49594
|
var LOW_EFFICIENCY_CATEGORY_ID = "recent_flow_red_streak";
|
|
48212
49595
|
var LOW_EFFICIENCY_AI_SUMMARY_ENABLED = process.env.NEXT_PUBLIC_LOW_EFFICIENCY_AI_SUMMARY_ENABLED === "true";
|
|
49596
|
+
var timeValueToMinutes2 = (value) => {
|
|
49597
|
+
const [hourValue, minuteValue] = value.substring(0, 5).split(":").map(Number);
|
|
49598
|
+
if (!Number.isInteger(hourValue) || !Number.isInteger(minuteValue) || hourValue < 0 || hourValue > 23 || minuteValue < 0 || minuteValue > 59) {
|
|
49599
|
+
return null;
|
|
49600
|
+
}
|
|
49601
|
+
return hourValue * 60 + minuteValue;
|
|
49602
|
+
};
|
|
49603
|
+
var isMinuteInTimeWindow2 = (minute, startValue, endValue) => {
|
|
49604
|
+
const startMinute = timeValueToMinutes2(startValue);
|
|
49605
|
+
const endMinute = timeValueToMinutes2(endValue);
|
|
49606
|
+
if (startMinute === null || endMinute === null) {
|
|
49607
|
+
return true;
|
|
49608
|
+
}
|
|
49609
|
+
return endMinute > startMinute ? minute >= startMinute && minute < endMinute : minute >= startMinute || minute < endMinute;
|
|
49610
|
+
};
|
|
49611
|
+
var CHART_HANDOFF_CATEGORY_FALLBACKS = {
|
|
49612
|
+
cycle_completion: {
|
|
49613
|
+
label: "Cycle Completion",
|
|
49614
|
+
description: "Successfully completed production cycles",
|
|
49615
|
+
color: "blue",
|
|
49616
|
+
icon: "check-circle"
|
|
49617
|
+
},
|
|
49618
|
+
"fast-cycles": {
|
|
49619
|
+
label: "Fast Cycles",
|
|
49620
|
+
description: "Fastest cycle clips",
|
|
49621
|
+
color: "green",
|
|
49622
|
+
icon: "trending-up"
|
|
49623
|
+
},
|
|
49624
|
+
"slow-cycles": {
|
|
49625
|
+
label: "Slow Cycles",
|
|
49626
|
+
description: "Slowest cycle clips",
|
|
49627
|
+
color: "red",
|
|
49628
|
+
icon: "trending-down"
|
|
49629
|
+
},
|
|
49630
|
+
idle_time: {
|
|
49631
|
+
label: "Idle Time",
|
|
49632
|
+
description: "Low value activities and idle moments",
|
|
49633
|
+
color: "amber",
|
|
49634
|
+
icon: "clock"
|
|
49635
|
+
},
|
|
49636
|
+
recent_flow_red_streak: {
|
|
49637
|
+
label: "Low moments",
|
|
49638
|
+
description: "Moments of low efficiency",
|
|
49639
|
+
color: "rose",
|
|
49640
|
+
icon: "alert-triangle"
|
|
49641
|
+
},
|
|
49642
|
+
worst_cycle_time: {
|
|
49643
|
+
label: "Slow Cycles",
|
|
49644
|
+
description: "Slowest cycle clips",
|
|
49645
|
+
color: "red",
|
|
49646
|
+
icon: "trending-down"
|
|
49647
|
+
},
|
|
49648
|
+
best_cycle_time: {
|
|
49649
|
+
label: "Fast Cycles",
|
|
49650
|
+
description: "Fastest cycle clips",
|
|
49651
|
+
color: "green",
|
|
49652
|
+
icon: "trending-up"
|
|
49653
|
+
}
|
|
49654
|
+
};
|
|
48213
49655
|
var parseFiniteNumber2 = (value) => {
|
|
48214
49656
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
48215
49657
|
return value;
|
|
@@ -48257,6 +49699,7 @@ var BottlenecksContent = ({
|
|
|
48257
49699
|
ticketId,
|
|
48258
49700
|
prefetchedPercentileCounts,
|
|
48259
49701
|
lowMomentsPrefetch,
|
|
49702
|
+
initialTimePrefetch,
|
|
48260
49703
|
initialTimeFilter
|
|
48261
49704
|
}) => {
|
|
48262
49705
|
console.log("\u{1F3AB} [BottlenecksContent] Rendered with ticketId:", ticketId || "NONE", "workspaceId:", workspaceId, "date:", date, "shift:", shift);
|
|
@@ -48469,6 +49912,108 @@ var BottlenecksContent = ({
|
|
|
48469
49912
|
enabled: isEffectiveShiftReady
|
|
48470
49913
|
});
|
|
48471
49914
|
const isLowMomentsCategoryAvailable = React125.useMemo(() => Array.isArray(clipTypes) && clipTypes.some((type) => type?.type === LOW_EFFICIENCY_CATEGORY_ID || type?.id === LOW_EFFICIENCY_CATEGORY_ID), [clipTypes]);
|
|
49915
|
+
const requestedInitialCategoryCandidates = React125.useMemo(() => {
|
|
49916
|
+
const requestedCategories = [
|
|
49917
|
+
...Array.isArray(initialTimeFilter?.categoryIds) ? initialTimeFilter.categoryIds : [],
|
|
49918
|
+
initialTimeFilter?.categoryId
|
|
49919
|
+
];
|
|
49920
|
+
return Array.from(
|
|
49921
|
+
new Set(
|
|
49922
|
+
requestedCategories.filter((category) => Boolean(category)).map((category) => category.trim()).filter(Boolean)
|
|
49923
|
+
)
|
|
49924
|
+
);
|
|
49925
|
+
}, [initialTimeFilter?.categoryId, initialTimeFilter?.categoryIds]);
|
|
49926
|
+
const hasInitialTimeHandoff = Boolean(initialTimeFilter?.startTime && initialTimeFilter?.endTime);
|
|
49927
|
+
const initialTimeHandoffCategorySet = React125.useMemo(() => new Set(requestedInitialCategoryCandidates), [requestedInitialCategoryCandidates]);
|
|
49928
|
+
const isInitialTimeHandoffCategory = React125.useCallback((categoryId) => Boolean(
|
|
49929
|
+
hasInitialTimeHandoff && categoryId && (initialTimeHandoffCategorySet.size === 0 || initialTimeHandoffCategorySet.has(categoryId))
|
|
49930
|
+
), [hasInitialTimeHandoff, initialTimeHandoffCategorySet]);
|
|
49931
|
+
const isTimestampInInitialTimeHandoff = React125.useCallback((timestamp) => {
|
|
49932
|
+
if (!hasInitialTimeHandoff || !initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !timestamp) {
|
|
49933
|
+
return true;
|
|
49934
|
+
}
|
|
49935
|
+
try {
|
|
49936
|
+
const clipDate = new Date(timestamp);
|
|
49937
|
+
if (Number.isNaN(clipDate.getTime())) {
|
|
49938
|
+
return false;
|
|
49939
|
+
}
|
|
49940
|
+
const clipParts = new Intl.DateTimeFormat("en-US", {
|
|
49941
|
+
hour12: false,
|
|
49942
|
+
hour: "2-digit",
|
|
49943
|
+
minute: "2-digit",
|
|
49944
|
+
timeZone: initialTimeFilter.timezone || timezone
|
|
49945
|
+
}).formatToParts(clipDate);
|
|
49946
|
+
const hourValue = clipParts.find((part) => part.type === "hour")?.value;
|
|
49947
|
+
const minuteValue = clipParts.find((part) => part.type === "minute")?.value;
|
|
49948
|
+
const clipMinute = timeValueToMinutes2(`${hourValue}:${minuteValue}`);
|
|
49949
|
+
return clipMinute === null ? false : isMinuteInTimeWindow2(clipMinute, initialTimeFilter.startTime, initialTimeFilter.endTime);
|
|
49950
|
+
} catch {
|
|
49951
|
+
return false;
|
|
49952
|
+
}
|
|
49953
|
+
}, [
|
|
49954
|
+
hasInitialTimeHandoff,
|
|
49955
|
+
initialTimeFilter?.endTime,
|
|
49956
|
+
initialTimeFilter?.startTime,
|
|
49957
|
+
initialTimeFilter?.timezone,
|
|
49958
|
+
timezone
|
|
49959
|
+
]);
|
|
49960
|
+
const resolvedInitialCategory = React125.useMemo(() => resolveInitialClipCategory(requestedInitialCategoryCandidates, clipTypes, dynamicCounts), [clipTypes, dynamicCounts, requestedInitialCategoryCandidates]);
|
|
49961
|
+
const initialTimeFilterKey = React125.useMemo(() => hasInitialTimeHandoff ? [
|
|
49962
|
+
initialTimeFilter?.startTime,
|
|
49963
|
+
initialTimeFilter?.endTime,
|
|
49964
|
+
initialTimeFilter?.timezone || timezone,
|
|
49965
|
+
initialTimeFilter?.categoryId || "",
|
|
49966
|
+
...initialTimeFilter?.categoryIds || []
|
|
49967
|
+
].join("|") : "", [
|
|
49968
|
+
hasInitialTimeHandoff,
|
|
49969
|
+
initialTimeFilter?.categoryId,
|
|
49970
|
+
initialTimeFilter?.categoryIds,
|
|
49971
|
+
initialTimeFilter?.endTime,
|
|
49972
|
+
initialTimeFilter?.startTime,
|
|
49973
|
+
initialTimeFilter?.timezone,
|
|
49974
|
+
timezone
|
|
49975
|
+
]);
|
|
49976
|
+
const preferredInitialTimeCategory = resolvedInitialCategory || requestedInitialCategoryCandidates[0] || "";
|
|
49977
|
+
React125.useEffect(() => {
|
|
49978
|
+
if (!initialTimeFilterKey) {
|
|
49979
|
+
return;
|
|
49980
|
+
}
|
|
49981
|
+
setPendingVideo(null);
|
|
49982
|
+
setAllVideos([]);
|
|
49983
|
+
setCurrentIndex(0);
|
|
49984
|
+
setCurrentClipId(null);
|
|
49985
|
+
currentClipIdRef.current = null;
|
|
49986
|
+
setCurrentMetadataIndex(0);
|
|
49987
|
+
currentMetadataIndexRef.current = 0;
|
|
49988
|
+
setCurrentPosition(0);
|
|
49989
|
+
currentPositionRef.current = 0;
|
|
49990
|
+
setCurrentTotal(0);
|
|
49991
|
+
currentTotalRef.current = 0;
|
|
49992
|
+
setCategoryMetadata([]);
|
|
49993
|
+
categoryMetadataRef.current = [];
|
|
49994
|
+
setCategoryMetadataCategoryId(null);
|
|
49995
|
+
setCategoryMetadataSort(null);
|
|
49996
|
+
clearRetryTimeout();
|
|
49997
|
+
navigationLockRef.current = false;
|
|
49998
|
+
loadingCategoryRef.current = null;
|
|
49999
|
+
if (loadingTimeoutRef.current) {
|
|
50000
|
+
clearTimeout(loadingTimeoutRef.current);
|
|
50001
|
+
loadingTimeoutRef.current = null;
|
|
50002
|
+
}
|
|
50003
|
+
setIsTransitioning(false);
|
|
50004
|
+
setIsNavigating(false);
|
|
50005
|
+
setIsCategoryLoading(false);
|
|
50006
|
+
if (preferredInitialTimeCategory) {
|
|
50007
|
+
setInitialFilter(preferredInitialTimeCategory);
|
|
50008
|
+
updateActiveFilter(preferredInitialTimeCategory);
|
|
50009
|
+
previousFilterRef.current = "";
|
|
50010
|
+
}
|
|
50011
|
+
}, [
|
|
50012
|
+
clearRetryTimeout,
|
|
50013
|
+
initialTimeFilterKey,
|
|
50014
|
+
preferredInitialTimeCategory,
|
|
50015
|
+
updateActiveFilter
|
|
50016
|
+
]);
|
|
48472
50017
|
console.log("[BottlenecksContent] Clip types data:", {
|
|
48473
50018
|
clipTypes,
|
|
48474
50019
|
clipTypesLength: clipTypes?.length,
|
|
@@ -48516,6 +50061,15 @@ var BottlenecksContent = ({
|
|
|
48516
50061
|
}
|
|
48517
50062
|
});
|
|
48518
50063
|
React125.useEffect(() => {
|
|
50064
|
+
if (resolvedInitialCategory) {
|
|
50065
|
+
if (initialFilter !== resolvedInitialCategory) {
|
|
50066
|
+
setInitialFilter(resolvedInitialCategory);
|
|
50067
|
+
}
|
|
50068
|
+
if (activeFilterRef.current !== resolvedInitialCategory) {
|
|
50069
|
+
updateActiveFilter(resolvedInitialCategory);
|
|
50070
|
+
}
|
|
50071
|
+
return;
|
|
50072
|
+
}
|
|
48519
50073
|
if (initialFilter) {
|
|
48520
50074
|
return;
|
|
48521
50075
|
}
|
|
@@ -48532,7 +50086,15 @@ var BottlenecksContent = ({
|
|
|
48532
50086
|
activeFilterRef.current = defaultCategory;
|
|
48533
50087
|
return;
|
|
48534
50088
|
}
|
|
48535
|
-
}, [
|
|
50089
|
+
}, [
|
|
50090
|
+
clipTypes,
|
|
50091
|
+
dynamicCounts,
|
|
50092
|
+
defaultCategory,
|
|
50093
|
+
initialFilter,
|
|
50094
|
+
isLowMomentsCategoryAvailable,
|
|
50095
|
+
resolvedInitialCategory,
|
|
50096
|
+
updateActiveFilter
|
|
50097
|
+
]);
|
|
48536
50098
|
const mergedCounts = React125.useMemo(() => {
|
|
48537
50099
|
return { ...dynamicCounts };
|
|
48538
50100
|
}, [dynamicCounts]);
|
|
@@ -48581,6 +50143,9 @@ var BottlenecksContent = ({
|
|
|
48581
50143
|
const loadFirstVideoForCategory = React125.useCallback(async (category) => {
|
|
48582
50144
|
if (!workspaceId || !s3ClipsService || !isMountedRef.current || !isEffectiveShiftReady) return;
|
|
48583
50145
|
const targetCategory = category || activeFilterRef.current;
|
|
50146
|
+
if (targetCategory === "fast-cycles" || targetCategory === "slow-cycles") {
|
|
50147
|
+
return;
|
|
50148
|
+
}
|
|
48584
50149
|
const operationKey = `loadFirstVideo:${targetCategory}:${effectiveDateString}:${effectiveShiftId}`;
|
|
48585
50150
|
if (loadingCategoryRef.current === targetCategory || fetchInProgressRef.current.has(operationKey)) {
|
|
48586
50151
|
console.log(`[BottlenecksContent] Load first video already in progress for ${targetCategory}`);
|
|
@@ -49026,8 +50591,9 @@ var BottlenecksContent = ({
|
|
|
49026
50591
|
});
|
|
49027
50592
|
}
|
|
49028
50593
|
setVisibleCategoryMetadata(categoryId, cachedMetadata);
|
|
49029
|
-
|
|
49030
|
-
|
|
50594
|
+
const cachedAutoloadCandidates = isInitialTimeHandoffCategory(categoryId) ? cachedMetadata.filter((clip) => isTimestampInInitialTimeHandoff(clip.clip_timestamp || clip.creation_timestamp || clip.timestamp)) : cachedMetadata;
|
|
50595
|
+
if (autoLoadFirstVideo && cachedAutoloadCandidates.length > 0 && s3ClipsService) {
|
|
50596
|
+
const firstClipMeta = cachedAutoloadCandidates[0];
|
|
49031
50597
|
const firstClipId = firstClipMeta.clipId || firstClipMeta.id;
|
|
49032
50598
|
const prefetchedFirstVideo = matchingLowMomentsPrefetch?.firstVideo ?? null;
|
|
49033
50599
|
try {
|
|
@@ -49066,7 +50632,10 @@ var BottlenecksContent = ({
|
|
|
49066
50632
|
endDate: `${resolvedDate}T23:59:59Z`,
|
|
49067
50633
|
percentile: 10,
|
|
49068
50634
|
shiftId: effectiveShiftId,
|
|
49069
|
-
limit: 100
|
|
50635
|
+
limit: isInitialTimeHandoffCategory(categoryId) ? 500 : 100,
|
|
50636
|
+
startTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.startTime : void 0,
|
|
50637
|
+
endTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.endTime : void 0,
|
|
50638
|
+
timeFilterTimezone: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.timezone || timezone : void 0
|
|
49070
50639
|
}),
|
|
49071
50640
|
redirectReason: "session_expired"
|
|
49072
50641
|
});
|
|
@@ -49083,10 +50652,13 @@ var BottlenecksContent = ({
|
|
|
49083
50652
|
shift: effectiveShiftId,
|
|
49084
50653
|
category: categoryId,
|
|
49085
50654
|
page: 1,
|
|
49086
|
-
limit: 100,
|
|
49087
|
-
knownTotal: mergedCounts[categoryId] ?? null,
|
|
50655
|
+
limit: isInitialTimeHandoffCategory(categoryId) ? 500 : 100,
|
|
50656
|
+
knownTotal: isInitialTimeHandoffCategory(categoryId) ? null : mergedCounts[categoryId] ?? null,
|
|
49088
50657
|
snapshotDateTime,
|
|
49089
50658
|
snapshotClipId,
|
|
50659
|
+
startTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.startTime : void 0,
|
|
50660
|
+
endTime: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.endTime : void 0,
|
|
50661
|
+
timeFilterTimezone: isInitialTimeHandoffCategory(categoryId) ? initialTimeFilter?.timezone || timezone : void 0,
|
|
49090
50662
|
sort: categoryId === LOW_EFFICIENCY_CATEGORY_ID ? "red_flow_output_shortfall_desc" : categoryId === "idle_time" ? idleClipSort : "latest"
|
|
49091
50663
|
}),
|
|
49092
50664
|
redirectReason: "session_expired"
|
|
@@ -49099,7 +50671,8 @@ var BottlenecksContent = ({
|
|
|
49099
50671
|
if (categoryData.clips && isMountedRef.current) {
|
|
49100
50672
|
let metadataClips;
|
|
49101
50673
|
if (isPercentileCategory(categoryId)) {
|
|
49102
|
-
|
|
50674
|
+
const sortedPercentileClips = sortPercentileCycleClipsForDisplay(categoryId, categoryData.clips);
|
|
50675
|
+
metadataClips = sortedPercentileClips.map((clip, index) => ({
|
|
49103
50676
|
id: clip.id,
|
|
49104
50677
|
clipId: clip.id,
|
|
49105
50678
|
clip_timestamp: clip.creation_timestamp || clip.timestamp,
|
|
@@ -49146,8 +50719,9 @@ var BottlenecksContent = ({
|
|
|
49146
50719
|
}));
|
|
49147
50720
|
setVisibleCategoryMetadata(categoryId, metadataClips);
|
|
49148
50721
|
console.log(`[BottlenecksContent] Loaded metadata for ${categoryId}: ${metadataClips.length} clips`);
|
|
49149
|
-
|
|
49150
|
-
|
|
50722
|
+
const autoloadCandidates = isInitialTimeHandoffCategory(categoryId) ? metadataClips.filter((clip) => isTimestampInInitialTimeHandoff(clip.clip_timestamp || clip.creation_timestamp || clip.timestamp)) : metadataClips;
|
|
50723
|
+
if (autoLoadFirstVideo && autoloadCandidates.length > 0 && s3ClipsService) {
|
|
50724
|
+
const firstClipMeta = autoloadCandidates[0];
|
|
49151
50725
|
const firstClipId = firstClipMeta.clipId || firstClipMeta.id;
|
|
49152
50726
|
try {
|
|
49153
50727
|
const video = await s3ClipsService.getClipById(firstClipId);
|
|
@@ -49181,7 +50755,7 @@ var BottlenecksContent = ({
|
|
|
49181
50755
|
setIsCategoryLoading(false);
|
|
49182
50756
|
}
|
|
49183
50757
|
}
|
|
49184
|
-
}, [workspaceId, effectiveDateString, effectiveShiftId, getMetadataCacheKey, isPercentileCategory, isFastSlowClipFiltersEnabled, metadataCache, s3ClipsService, clearLoadingState, isEffectiveShiftReady, snapshotDateTime, snapshotClipId, idleClipSort, supabase, setVisibleCategoryMetadata, lowMomentsPrefetch, applyPrefetchedFirstVideo, applyMetadataSnapshot, isLowMomentsCategoryAvailable]);
|
|
50758
|
+
}, [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]);
|
|
49185
50759
|
React125.useEffect(() => {
|
|
49186
50760
|
if (activeFilter !== LOW_EFFICIENCY_CATEGORY_ID || !isLowMomentsCategoryAvailable) {
|
|
49187
50761
|
return;
|
|
@@ -49213,6 +50787,37 @@ var BottlenecksContent = ({
|
|
|
49213
50787
|
applyMetadataSnapshot,
|
|
49214
50788
|
isLowMomentsCategoryAvailable
|
|
49215
50789
|
]);
|
|
50790
|
+
React125.useEffect(() => {
|
|
50791
|
+
if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !initialTimePrefetch || initialTimePrefetch.loading || initialTimePrefetch.categoryId !== activeFilter) {
|
|
50792
|
+
return;
|
|
50793
|
+
}
|
|
50794
|
+
if (initialTimePrefetch.metadata.length > 0) {
|
|
50795
|
+
applyMetadataSnapshot(
|
|
50796
|
+
initialTimePrefetch.categoryId,
|
|
50797
|
+
initialTimePrefetch.metadata,
|
|
50798
|
+
initialTimePrefetch.total
|
|
50799
|
+
);
|
|
50800
|
+
}
|
|
50801
|
+
if (initialTimePrefetch.firstVideo && isTimestampInInitialTimeHandoff(
|
|
50802
|
+
initialTimePrefetch.firstVideo.creation_timestamp || initialTimePrefetch.firstVideo.timestamp
|
|
50803
|
+
)) {
|
|
50804
|
+
applyPrefetchedFirstVideo(initialTimePrefetch.firstVideo);
|
|
50805
|
+
if (isMountedRef.current) {
|
|
50806
|
+
setIsCategoryLoading(false);
|
|
50807
|
+
setIsInitialLoading(false);
|
|
50808
|
+
setIsTransitioning(false);
|
|
50809
|
+
setIsNavigating(false);
|
|
50810
|
+
}
|
|
50811
|
+
}
|
|
50812
|
+
}, [
|
|
50813
|
+
activeFilter,
|
|
50814
|
+
applyMetadataSnapshot,
|
|
50815
|
+
applyPrefetchedFirstVideo,
|
|
50816
|
+
initialTimeFilter?.endTime,
|
|
50817
|
+
initialTimeFilter?.startTime,
|
|
50818
|
+
initialTimePrefetch,
|
|
50819
|
+
isTimestampInInitialTimeHandoff
|
|
50820
|
+
]);
|
|
49216
50821
|
React125.useEffect(() => {
|
|
49217
50822
|
if (previousIdleClipSortRef.current === idleClipSort) {
|
|
49218
50823
|
return;
|
|
@@ -49252,7 +50857,7 @@ var BottlenecksContent = ({
|
|
|
49252
50857
|
currentTotalRef.current = total;
|
|
49253
50858
|
setCurrentTotal(total);
|
|
49254
50859
|
previousFilterRef.current = activeFilter;
|
|
49255
|
-
const metadataLoadPlan = getCategoryMetadataLoadPlanForFilterChange({
|
|
50860
|
+
const metadataLoadPlan = isInitialTimeHandoffCategory(activeFilter) ? { shouldLoad: false, autoLoadFirstVideo: false } : getCategoryMetadataLoadPlanForFilterChange({
|
|
49256
50861
|
activeFilter,
|
|
49257
50862
|
currentClipId,
|
|
49258
50863
|
categoryTotal: total
|
|
@@ -49276,7 +50881,7 @@ var BottlenecksContent = ({
|
|
|
49276
50881
|
}
|
|
49277
50882
|
}
|
|
49278
50883
|
}
|
|
49279
|
-
}, [activeFilter, allVideos, mergedCounts, currentClipId, loadCategoryMetadata]);
|
|
50884
|
+
}, [activeFilter, allVideos, mergedCounts, currentClipId, loadCategoryMetadata, isInitialTimeHandoffCategory]);
|
|
49280
50885
|
React125.useEffect(() => {
|
|
49281
50886
|
if (!currentClipId || activeFilter === "all") {
|
|
49282
50887
|
return;
|
|
@@ -49304,10 +50909,19 @@ var BottlenecksContent = ({
|
|
|
49304
50909
|
console.warn("[BottlenecksContent] Error disposing player:", e);
|
|
49305
50910
|
}
|
|
49306
50911
|
}
|
|
49307
|
-
loadingTimeoutRef.current
|
|
50912
|
+
if (loadingTimeoutRef.current) {
|
|
50913
|
+
clearTimeout(loadingTimeoutRef.current);
|
|
50914
|
+
loadingTimeoutRef.current = null;
|
|
50915
|
+
}
|
|
50916
|
+
const loadingTimeout = setTimeout(() => {
|
|
50917
|
+
if (loadingTimeoutRef.current !== loadingTimeout) {
|
|
50918
|
+
return;
|
|
50919
|
+
}
|
|
50920
|
+
loadingTimeoutRef.current = null;
|
|
49308
50921
|
console.warn("[BottlenecksContent] Loading timeout - clearing stuck loading state");
|
|
49309
50922
|
clearLoadingState();
|
|
49310
50923
|
}, 2e3);
|
|
50924
|
+
loadingTimeoutRef.current = loadingTimeout;
|
|
49311
50925
|
if (activeFilterRef.current !== categoryId) {
|
|
49312
50926
|
updateActiveFilter(categoryId);
|
|
49313
50927
|
}
|
|
@@ -49327,17 +50941,20 @@ var BottlenecksContent = ({
|
|
|
49327
50941
|
setCurrentClipId(clipId);
|
|
49328
50942
|
setAllVideos([video]);
|
|
49329
50943
|
setCurrentIndex(0);
|
|
50944
|
+
clearLoadingState();
|
|
49330
50945
|
} else {
|
|
49331
50946
|
throw new Error(`Failed to load video data for clip ${clipId}`);
|
|
49332
50947
|
}
|
|
49333
50948
|
await metadataPromise;
|
|
49334
50949
|
let metadataArray = categoryMetadataRef.current;
|
|
49335
|
-
const
|
|
49336
|
-
|
|
50950
|
+
const getMetadataClipId = (clip) => clip?.clipId || clip?.id;
|
|
50951
|
+
const metadataHasClip = (clips) => clips.some((clip) => getMetadataClipId(clip) === clipId);
|
|
50952
|
+
const fallbackHasClip = fallbackMetadata?.some((clip) => getMetadataClipId(clip) === clipId);
|
|
50953
|
+
if ((metadataArray.length === 0 || !metadataHasClip(metadataArray)) && fallbackHasClip) {
|
|
49337
50954
|
applyMetadataSnapshot(categoryId, fallbackMetadata, fallbackTotal);
|
|
49338
50955
|
metadataArray = fallbackMetadata;
|
|
49339
50956
|
}
|
|
49340
|
-
if (metadataArray.length === 0 || !metadataArray
|
|
50957
|
+
if (metadataArray.length === 0 || !metadataHasClip(metadataArray)) {
|
|
49341
50958
|
if (!fallbackHasClip) {
|
|
49342
50959
|
console.warn(`[BottlenecksContent] Clip ${clipId} not found in metadata for ${categoryId} (cache hit: ${metadataArray.length > 0}) - forcing refresh`);
|
|
49343
50960
|
await loadCategoryMetadata(categoryId, false, true);
|
|
@@ -49356,7 +50973,7 @@ var BottlenecksContent = ({
|
|
|
49356
50973
|
}
|
|
49357
50974
|
return;
|
|
49358
50975
|
}
|
|
49359
|
-
const clickedClipIndex = metadataArray.findIndex((clip) => clip
|
|
50976
|
+
const clickedClipIndex = metadataArray.findIndex((clip) => getMetadataClipId(clip) === clipId);
|
|
49360
50977
|
if (clickedClipIndex === -1) {
|
|
49361
50978
|
console.warn(`[BottlenecksContent] Clip ${clipId} not found after metadata refresh`);
|
|
49362
50979
|
if (!shouldUseMetadataNavigation(categoryId)) {
|
|
@@ -49744,6 +51361,32 @@ var BottlenecksContent = ({
|
|
|
49744
51361
|
}
|
|
49745
51362
|
return filteredVideos[currentIndex];
|
|
49746
51363
|
}, [filteredVideos, currentIndex]);
|
|
51364
|
+
const currentVideoMatchesInitialTimeHandoff = React125.useMemo(() => {
|
|
51365
|
+
if (!hasInitialTimeHandoff) {
|
|
51366
|
+
return true;
|
|
51367
|
+
}
|
|
51368
|
+
if (!currentVideo) {
|
|
51369
|
+
return false;
|
|
51370
|
+
}
|
|
51371
|
+
if (!isInitialTimeHandoffCategory(activeFilter) || !isInitialTimeHandoffCategory(currentVideo.type)) {
|
|
51372
|
+
return false;
|
|
51373
|
+
}
|
|
51374
|
+
return isTimestampInInitialTimeHandoff(
|
|
51375
|
+
currentVideo.creation_timestamp || currentVideo.timestamp || currentVideo.clip_end_time || currentVideo.clip_start_time
|
|
51376
|
+
);
|
|
51377
|
+
}, [
|
|
51378
|
+
activeFilter,
|
|
51379
|
+
currentVideo,
|
|
51380
|
+
hasInitialTimeHandoff,
|
|
51381
|
+
isInitialTimeHandoffCategory,
|
|
51382
|
+
isTimestampInInitialTimeHandoff
|
|
51383
|
+
]);
|
|
51384
|
+
const shouldHoldInitialTimeHandoffVideo = Boolean(
|
|
51385
|
+
hasInitialTimeHandoff && !currentVideoMatchesInitialTimeHandoff && (isCategoryLoading || filteredVideos.length > 0 || (mergedCounts[activeFilter] || 0) > 0)
|
|
51386
|
+
);
|
|
51387
|
+
const canRenderCurrentVideo = Boolean(
|
|
51388
|
+
filteredVideos.length > 0 && currentVideo && !isFullscreen && !shouldHoldInitialTimeHandoffVideo
|
|
51389
|
+
);
|
|
49747
51390
|
const currentLowEfficiencyClipId = currentVideo?.id || currentClipId || null;
|
|
49748
51391
|
const isCurrentLowEfficiencyClip = Boolean(
|
|
49749
51392
|
currentVideo?.type === "recent_flow_red_streak" || currentVideo?.red_flow_timeline
|
|
@@ -49910,11 +51553,111 @@ var BottlenecksContent = ({
|
|
|
49910
51553
|
}
|
|
49911
51554
|
return currentPosition;
|
|
49912
51555
|
}, [activeFilter, categoryMetadata.length, currentMetadataIndex, currentPosition, shouldUseMetadataNavigation]);
|
|
49913
|
-
const
|
|
51556
|
+
const initialTimePrefetchedMetadata = React125.useMemo(() => {
|
|
51557
|
+
if (!initialTimeFilter?.startTime || !initialTimeFilter?.endTime || !initialTimePrefetch || initialTimePrefetch.loading) {
|
|
51558
|
+
return void 0;
|
|
51559
|
+
}
|
|
51560
|
+
if (initialTimePrefetch.metadataByCategory) {
|
|
51561
|
+
return initialTimePrefetch.metadataByCategory;
|
|
51562
|
+
}
|
|
51563
|
+
if (initialTimePrefetch.categoryId === activeFilter && initialTimePrefetch.metadata.length > 0) {
|
|
51564
|
+
return { [activeFilter]: initialTimePrefetch.metadata };
|
|
51565
|
+
}
|
|
51566
|
+
return void 0;
|
|
51567
|
+
}, [
|
|
49914
51568
|
activeFilter,
|
|
51569
|
+
initialTimePrefetch,
|
|
51570
|
+
initialTimeFilter?.endTime,
|
|
51571
|
+
initialTimeFilter?.startTime
|
|
51572
|
+
]);
|
|
51573
|
+
const prefetchedExplorerMetadata = React125.useMemo(() => {
|
|
51574
|
+
if (initialTimePrefetchedMetadata) {
|
|
51575
|
+
return initialTimePrefetchedMetadata;
|
|
51576
|
+
}
|
|
51577
|
+
if (activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && !lowMomentsPrefetch.loading && lowMomentsPrefetch.metadata.length > 0) {
|
|
51578
|
+
return { [LOW_EFFICIENCY_CATEGORY_ID]: lowMomentsPrefetch.metadata };
|
|
51579
|
+
}
|
|
51580
|
+
if (activeFilter === "idle_time" && categoryMetadataSort !== idleClipSort) {
|
|
51581
|
+
return void 0;
|
|
51582
|
+
}
|
|
51583
|
+
const metadataSnapshot = buildPrefetchedExplorerMetadata(
|
|
51584
|
+
activeFilter,
|
|
51585
|
+
categoryMetadataCategoryId,
|
|
51586
|
+
categoryMetadata
|
|
51587
|
+
);
|
|
51588
|
+
if (metadataSnapshot) {
|
|
51589
|
+
return metadataSnapshot;
|
|
51590
|
+
}
|
|
51591
|
+
if (initialTimeFilter?.startTime && initialTimeFilter?.endTime && currentVideo && currentVideo.type === activeFilter) {
|
|
51592
|
+
return {
|
|
51593
|
+
[activeFilter]: [{
|
|
51594
|
+
id: currentVideo.id,
|
|
51595
|
+
clipId: currentVideo.id,
|
|
51596
|
+
clip_timestamp: currentVideo.creation_timestamp || currentVideo.timestamp,
|
|
51597
|
+
description: currentVideo.description,
|
|
51598
|
+
severity: currentVideo.severity,
|
|
51599
|
+
category: activeFilter,
|
|
51600
|
+
duration: typeof currentVideo.duration === "number" ? currentVideo.duration : typeof currentVideo.cycle_time_seconds === "number" ? currentVideo.cycle_time_seconds : void 0,
|
|
51601
|
+
clip_start_time: currentVideo.clip_start_time,
|
|
51602
|
+
clip_end_time: currentVideo.clip_end_time,
|
|
51603
|
+
index: 0,
|
|
51604
|
+
idle_start_time: currentVideo.idle_start_time,
|
|
51605
|
+
idle_end_time: currentVideo.idle_end_time,
|
|
51606
|
+
cycle_item_count: null,
|
|
51607
|
+
red_flow_timeline: currentVideo.red_flow_timeline,
|
|
51608
|
+
red_flow_severity_score: currentVideo.red_flow_severity_score,
|
|
51609
|
+
red_flow_output_shortfall_units: currentVideo.red_flow_output_shortfall_units,
|
|
51610
|
+
red_flow_worst_minute: currentVideo.red_flow_worst_minute,
|
|
51611
|
+
red_flow_explanation_summary: currentVideo.red_flow_explanation_summary,
|
|
51612
|
+
red_flow_explanation: currentVideo.red_flow_explanation
|
|
51613
|
+
}]
|
|
51614
|
+
};
|
|
51615
|
+
}
|
|
51616
|
+
return void 0;
|
|
51617
|
+
}, [
|
|
51618
|
+
activeFilter,
|
|
51619
|
+
categoryMetadata,
|
|
49915
51620
|
categoryMetadataCategoryId,
|
|
49916
|
-
|
|
49917
|
-
|
|
51621
|
+
categoryMetadataSort,
|
|
51622
|
+
currentVideo,
|
|
51623
|
+
idleClipSort,
|
|
51624
|
+
initialTimePrefetchedMetadata,
|
|
51625
|
+
initialTimePrefetch,
|
|
51626
|
+
initialTimeFilter?.endTime,
|
|
51627
|
+
initialTimeFilter?.startTime,
|
|
51628
|
+
lowMomentsPrefetch,
|
|
51629
|
+
workspaceId,
|
|
51630
|
+
effectiveDateString,
|
|
51631
|
+
effectiveShiftId,
|
|
51632
|
+
isLowMomentsCategoryAvailable
|
|
51633
|
+
]);
|
|
51634
|
+
const externallyManagedLoadingCategories = React125.useMemo(() => {
|
|
51635
|
+
const managedCategories = {
|
|
51636
|
+
recent_flow_red_streak: Boolean(
|
|
51637
|
+
activeFilter === LOW_EFFICIENCY_CATEGORY_ID && isLowMomentsCategoryAvailable && lowMomentsPrefetch?.key?.startsWith(`${LOW_EFFICIENCY_CATEGORY_ID}-${workspaceId}-${effectiveDateString}-${effectiveShiftId}-`) && lowMomentsPrefetch.loading
|
|
51638
|
+
)
|
|
51639
|
+
};
|
|
51640
|
+
const isInitialTimeHandoffPending = Boolean(
|
|
51641
|
+
initialTimeFilter?.startTime && initialTimeFilter?.endTime && requestedInitialCategoryCandidates.length > 0 && (!initialTimePrefetch || initialTimePrefetch.loading)
|
|
51642
|
+
);
|
|
51643
|
+
if (isInitialTimeHandoffPending) {
|
|
51644
|
+
requestedInitialCategoryCandidates.forEach((categoryId) => {
|
|
51645
|
+
managedCategories[categoryId] = true;
|
|
51646
|
+
});
|
|
51647
|
+
}
|
|
51648
|
+
return managedCategories;
|
|
51649
|
+
}, [
|
|
51650
|
+
activeFilter,
|
|
51651
|
+
effectiveDateString,
|
|
51652
|
+
effectiveShiftId,
|
|
51653
|
+
initialTimeFilter?.endTime,
|
|
51654
|
+
initialTimeFilter?.startTime,
|
|
51655
|
+
initialTimePrefetch,
|
|
51656
|
+
isLowMomentsCategoryAvailable,
|
|
51657
|
+
lowMomentsPrefetch,
|
|
51658
|
+
requestedInitialCategoryCandidates,
|
|
51659
|
+
workspaceId
|
|
51660
|
+
]);
|
|
49918
51661
|
const classificationClipIds = React125.useMemo(() => {
|
|
49919
51662
|
if (!idleTimeVlmEnabled) {
|
|
49920
51663
|
return [];
|
|
@@ -50324,7 +52067,7 @@ var BottlenecksContent = ({
|
|
|
50324
52067
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: "S3 configuration is required to load video clips. Please check your dashboard configuration." })
|
|
50325
52068
|
] });
|
|
50326
52069
|
}
|
|
50327
|
-
if (clipTypesLoading && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
|
|
52070
|
+
if (!hasInitialTimeHandoff && clipTypesLoading && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
|
|
50328
52071
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow p-4 flex items-center justify-center min-h-[calc(100dvh-12rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
|
|
50329
52072
|
}
|
|
50330
52073
|
if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
|
|
@@ -50334,7 +52077,30 @@ var BottlenecksContent = ({
|
|
|
50334
52077
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
|
|
50335
52078
|
] });
|
|
50336
52079
|
}
|
|
50337
|
-
const categoriesToShow =
|
|
52080
|
+
const categoriesToShow = (() => {
|
|
52081
|
+
const categories = clipTypes.length > 0 ? [...clipTypes] : [];
|
|
52082
|
+
const existingTypes = new Set(categories.map((category) => category.type));
|
|
52083
|
+
requestedInitialCategoryCandidates.forEach((categoryType) => {
|
|
52084
|
+
if (existingTypes.has(categoryType) || !hasInitialTimeHandoff && (mergedCounts[categoryType] || 0) <= 0) {
|
|
52085
|
+
return;
|
|
52086
|
+
}
|
|
52087
|
+
const fallback = CHART_HANDOFF_CATEGORY_FALLBACKS[categoryType];
|
|
52088
|
+
if (!fallback) {
|
|
52089
|
+
return;
|
|
52090
|
+
}
|
|
52091
|
+
categories.push({
|
|
52092
|
+
id: categoryType,
|
|
52093
|
+
type: categoryType,
|
|
52094
|
+
label: fallback.label,
|
|
52095
|
+
description: fallback.description,
|
|
52096
|
+
color: fallback.color,
|
|
52097
|
+
icon: fallback.icon,
|
|
52098
|
+
sort_order: 50
|
|
52099
|
+
});
|
|
52100
|
+
existingTypes.add(categoryType);
|
|
52101
|
+
});
|
|
52102
|
+
return categories;
|
|
52103
|
+
})();
|
|
50338
52104
|
console.log("[BottlenecksContent] Categories to show:", {
|
|
50339
52105
|
categoriesToShow,
|
|
50340
52106
|
categoriesToShowLength: categoriesToShow?.length,
|
|
@@ -50353,190 +52119,204 @@ var BottlenecksContent = ({
|
|
|
50353
52119
|
}
|
|
50354
52120
|
),
|
|
50355
52121
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 lg:flex-row lg:h-full", children: [
|
|
50356
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
50357
|
-
|
|
50358
|
-
|
|
50359
|
-
|
|
50360
|
-
|
|
50361
|
-
|
|
50362
|
-
|
|
50363
|
-
|
|
50364
|
-
|
|
50365
|
-
|
|
50366
|
-
|
|
52122
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
52123
|
+
"div",
|
|
52124
|
+
{
|
|
52125
|
+
className: "min-w-0 w-full lg:flex-[3] lg:h-full",
|
|
52126
|
+
"data-qa-clips-active-filter": activeFilter || "",
|
|
52127
|
+
"data-qa-clips-current-video-type": currentVideo?.type || "",
|
|
52128
|
+
"data-qa-clips-current-video-id": currentVideo?.id || "",
|
|
52129
|
+
"data-qa-clips-current-clip-id": currentClipId || "",
|
|
52130
|
+
"data-qa-clips-filtered-count": filteredVideos.length,
|
|
52131
|
+
"data-qa-clips-all-count": allVideos.length,
|
|
52132
|
+
"data-qa-clips-current-video-matches-hour": String(currentVideoMatchesInitialTimeHandoff),
|
|
52133
|
+
"data-qa-clips-hold-hour-video": String(shouldHoldInitialTimeHandoffVideo),
|
|
52134
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-sm overflow-hidden lg:h-full", children: canRenderCurrentVideo && currentVideo ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative group lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full aspect-video lg:aspect-auto lg:h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
|
|
52135
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
52136
|
+
"div",
|
|
50367
52137
|
{
|
|
50368
|
-
ref: videoRef,
|
|
50369
|
-
src: currentVideo.src,
|
|
50370
|
-
poster: "",
|
|
50371
52138
|
className: "w-full h-full",
|
|
50372
|
-
|
|
50373
|
-
|
|
50374
|
-
|
|
50375
|
-
loop: false,
|
|
50376
|
-
externalLoadingControl: true,
|
|
50377
|
-
onReady: handleVideoReady,
|
|
50378
|
-
onPlay: handleVideoPlay,
|
|
50379
|
-
onPause: handleVideoPause,
|
|
50380
|
-
onTimeUpdate: handleTimeUpdate,
|
|
50381
|
-
onDurationChange: handleDurationChange,
|
|
50382
|
-
onEnded: handleVideoEnded,
|
|
50383
|
-
onError: handleVideoError,
|
|
50384
|
-
onLoadedData: handleLoadedData,
|
|
50385
|
-
onPlaying: handleVideoPlaying,
|
|
50386
|
-
onLoadingChange: handleVideoLoadingChange,
|
|
50387
|
-
onShare: handleShareClip,
|
|
50388
|
-
isShareLoading,
|
|
50389
|
-
isShareCopied,
|
|
50390
|
-
timelineAnnotations: currentVideo.red_flow_timeline,
|
|
50391
|
-
timelineExplanation: currentVideo.red_flow_explanation,
|
|
50392
|
-
timelineTimezone: timezone,
|
|
50393
|
-
options: videoPlayerOptions
|
|
50394
|
-
},
|
|
50395
|
-
`${currentVideo.id}-${playerInstanceNonce}-inline`
|
|
50396
|
-
)
|
|
50397
|
-
}
|
|
50398
|
-
),
|
|
50399
|
-
currentVideo.type === "recent_flow_red_streak" && !shouldDeferPlayerRenderForCrop ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
50400
|
-
RedFlowDiagnosticOverlay,
|
|
50401
|
-
{
|
|
50402
|
-
timeline: currentVideo.red_flow_timeline,
|
|
50403
|
-
explanation: currentVideo.red_flow_explanation,
|
|
50404
|
-
aiSummary: currentLowEfficiencyAiSummary,
|
|
50405
|
-
aiSummaryLoading: isCurrentLowEfficiencyAiSummaryLoading,
|
|
50406
|
-
aiSummaryError: currentLowEfficiencyAiSummaryError,
|
|
50407
|
-
className: "right-4 top-4"
|
|
50408
|
-
}
|
|
50409
|
-
) : null,
|
|
50410
|
-
showBlockingVideoLoader && !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
50411
|
-
!shouldDeferPlayerRenderForCrop && !isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
50412
|
-
error && error.type === "retrying" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
50413
|
-
/* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md" }),
|
|
50414
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
|
|
50415
|
-
] }) }),
|
|
50416
|
-
error && error.type === "fatal" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/90 text-white p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-md", children: [
|
|
50417
|
-
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
50418
|
-
/* @__PURE__ */ jsxRuntime.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" }),
|
|
50419
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
|
|
50420
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 justify-center", children: [
|
|
50421
|
-
error.canSkip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
50422
|
-
"button",
|
|
50423
|
-
{
|
|
50424
|
-
onClick: () => {
|
|
50425
|
-
setError(null);
|
|
50426
|
-
videoRetryCountRef.current = 0;
|
|
50427
|
-
handleNext();
|
|
52139
|
+
style: {
|
|
52140
|
+
opacity: isTransitioning ? 0 : 1,
|
|
52141
|
+
transition: "opacity 0.1s ease-in-out"
|
|
50428
52142
|
},
|
|
50429
|
-
|
|
50430
|
-
|
|
52143
|
+
children: !shouldDeferPlayerRenderForCrop && /* @__PURE__ */ jsxRuntime.jsx(
|
|
52144
|
+
CroppedVideoPlayer,
|
|
52145
|
+
{
|
|
52146
|
+
ref: videoRef,
|
|
52147
|
+
src: currentVideo.src,
|
|
52148
|
+
poster: "",
|
|
52149
|
+
className: "w-full h-full",
|
|
52150
|
+
crop: workspaceCrop?.crop,
|
|
52151
|
+
autoplay: true,
|
|
52152
|
+
playsInline: true,
|
|
52153
|
+
loop: false,
|
|
52154
|
+
externalLoadingControl: true,
|
|
52155
|
+
onReady: handleVideoReady,
|
|
52156
|
+
onPlay: handleVideoPlay,
|
|
52157
|
+
onPause: handleVideoPause,
|
|
52158
|
+
onTimeUpdate: handleTimeUpdate,
|
|
52159
|
+
onDurationChange: handleDurationChange,
|
|
52160
|
+
onEnded: handleVideoEnded,
|
|
52161
|
+
onError: handleVideoError,
|
|
52162
|
+
onLoadedData: handleLoadedData,
|
|
52163
|
+
onPlaying: handleVideoPlaying,
|
|
52164
|
+
onLoadingChange: handleVideoLoadingChange,
|
|
52165
|
+
onShare: handleShareClip,
|
|
52166
|
+
isShareLoading,
|
|
52167
|
+
isShareCopied,
|
|
52168
|
+
timelineAnnotations: currentVideo.red_flow_timeline,
|
|
52169
|
+
timelineExplanation: currentVideo.red_flow_explanation,
|
|
52170
|
+
timelineTimezone: timezone,
|
|
52171
|
+
options: videoPlayerOptions
|
|
52172
|
+
},
|
|
52173
|
+
`${currentVideo.id}-${playerInstanceNonce}-inline`
|
|
52174
|
+
)
|
|
50431
52175
|
}
|
|
50432
52176
|
),
|
|
50433
|
-
|
|
50434
|
-
|
|
52177
|
+
currentVideo.type === "recent_flow_red_streak" && !shouldDeferPlayerRenderForCrop ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
52178
|
+
RedFlowDiagnosticOverlay,
|
|
50435
52179
|
{
|
|
50436
|
-
|
|
50437
|
-
|
|
50438
|
-
|
|
50439
|
-
|
|
50440
|
-
|
|
50441
|
-
|
|
52180
|
+
timeline: currentVideo.red_flow_timeline,
|
|
52181
|
+
explanation: currentVideo.red_flow_explanation,
|
|
52182
|
+
aiSummary: currentLowEfficiencyAiSummary,
|
|
52183
|
+
aiSummaryLoading: isCurrentLowEfficiencyAiSummaryLoading,
|
|
52184
|
+
aiSummaryError: currentLowEfficiencyAiSummaryError,
|
|
52185
|
+
className: "right-4 top-4"
|
|
50442
52186
|
}
|
|
50443
|
-
)
|
|
50444
|
-
|
|
50445
|
-
|
|
50446
|
-
|
|
50447
|
-
|
|
50448
|
-
|
|
50449
|
-
|
|
50450
|
-
|
|
50451
|
-
|
|
50452
|
-
|
|
50453
|
-
|
|
50454
|
-
|
|
50455
|
-
|
|
50456
|
-
|
|
50457
|
-
|
|
50458
|
-
|
|
50459
|
-
|
|
50460
|
-
|
|
50461
|
-
|
|
50462
|
-
|
|
50463
|
-
|
|
50464
|
-
|
|
50465
|
-
|
|
50466
|
-
|
|
50467
|
-
|
|
50468
|
-
|
|
50469
|
-
|
|
50470
|
-
|
|
50471
|
-
|
|
50472
|
-
|
|
50473
|
-
|
|
52187
|
+
) : null,
|
|
52188
|
+
showBlockingVideoLoader && !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
52189
|
+
!shouldDeferPlayerRenderForCrop && !isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
52190
|
+
error && error.type === "retrying" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
52191
|
+
/* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md" }),
|
|
52192
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
|
|
52193
|
+
] }) }),
|
|
52194
|
+
error && error.type === "fatal" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/90 text-white p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-md", children: [
|
|
52195
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
52196
|
+
/* @__PURE__ */ jsxRuntime.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" }),
|
|
52197
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
|
|
52198
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 justify-center", children: [
|
|
52199
|
+
error.canSkip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
52200
|
+
"button",
|
|
52201
|
+
{
|
|
52202
|
+
onClick: () => {
|
|
52203
|
+
setError(null);
|
|
52204
|
+
videoRetryCountRef.current = 0;
|
|
52205
|
+
handleNext();
|
|
52206
|
+
},
|
|
52207
|
+
className: "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 rounded-md text-sm font-medium transition-colors",
|
|
52208
|
+
children: "Skip to Next Clip"
|
|
52209
|
+
}
|
|
52210
|
+
),
|
|
52211
|
+
error.canRetry && /* @__PURE__ */ jsxRuntime.jsx(
|
|
52212
|
+
"button",
|
|
52213
|
+
{
|
|
52214
|
+
onClick: () => {
|
|
52215
|
+
videoRetryCountRef.current = 0;
|
|
52216
|
+
restartCurrentClipPlayback();
|
|
52217
|
+
},
|
|
52218
|
+
className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
|
|
52219
|
+
children: "Retry"
|
|
52220
|
+
}
|
|
52221
|
+
)
|
|
52222
|
+
] })
|
|
52223
|
+
] }) }),
|
|
52224
|
+
(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" ? (
|
|
52225
|
+
// Show full colored badge for idle time
|
|
52226
|
+
(() => {
|
|
52227
|
+
const classification = getIdleTimeClassification(currentVideo);
|
|
52228
|
+
const confidence = getIdleTimeConfidence(currentVideo);
|
|
52229
|
+
const config = getRootCauseConfig(classification);
|
|
52230
|
+
if (!config) return null;
|
|
52231
|
+
const IconComponent = config.Icon;
|
|
52232
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
52233
|
+
idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsxRuntime.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: [
|
|
52234
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
|
|
52235
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
|
|
52236
|
+
] }) }),
|
|
52237
|
+
idleTimeVlmEnabled && confidence !== null && (() => {
|
|
52238
|
+
const confidencePercent = confidence * 100;
|
|
52239
|
+
let confidenceLabel = "Low";
|
|
52240
|
+
let confidenceColor = "text-red-500";
|
|
52241
|
+
if (confidencePercent > 95) {
|
|
52242
|
+
confidenceLabel = "High";
|
|
52243
|
+
confidenceColor = "text-green-500";
|
|
52244
|
+
} else if (confidencePercent >= 91) {
|
|
52245
|
+
confidenceLabel = "Medium";
|
|
52246
|
+
confidenceColor = "text-yellow-500";
|
|
52247
|
+
}
|
|
52248
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 right-3 z-10", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("span", { className: "font-medium text-xs text-white", children: [
|
|
52249
|
+
"AI Confidence: ",
|
|
52250
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: confidenceColor, children: confidenceLabel })
|
|
52251
|
+
] }) }) });
|
|
52252
|
+
})()
|
|
52253
|
+
] });
|
|
50474
52254
|
})()
|
|
50475
|
-
|
|
50476
|
-
|
|
50477
|
-
|
|
50478
|
-
|
|
50479
|
-
|
|
50480
|
-
|
|
50481
|
-
|
|
50482
|
-
|
|
50483
|
-
|
|
50484
|
-
|
|
50485
|
-
|
|
50486
|
-
|
|
50487
|
-
|
|
50488
|
-
|
|
50489
|
-
|
|
50490
|
-
|
|
50491
|
-
|
|
50492
|
-
|
|
50493
|
-
|
|
50494
|
-
|
|
50495
|
-
|
|
50496
|
-
|
|
50497
|
-
|
|
50498
|
-
|
|
50499
|
-
|
|
50500
|
-
|
|
50501
|
-
|
|
50502
|
-
|
|
50503
|
-
|
|
50504
|
-
|
|
50505
|
-
|
|
50506
|
-
|
|
50507
|
-
|
|
50508
|
-
|
|
50509
|
-
|
|
50510
|
-
|
|
50511
|
-
|
|
50512
|
-
|
|
50513
|
-
] }) })
|
|
50514
|
-
|
|
50515
|
-
|
|
50516
|
-
/* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
|
|
50517
|
-
hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
50518
|
-
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
50519
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
|
|
50520
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
|
|
50521
|
-
] }) }) : (
|
|
50522
|
-
/* Priority 5.5: Show "no folder selected" if activeFilter is empty */
|
|
50523
|
-
hasInitialLoad && !activeFilter ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
50524
|
-
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
50525
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Folder Selected" }),
|
|
50526
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "Please select a folder to view clips." })
|
|
50527
|
-
] }) }) : (
|
|
50528
|
-
/* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
|
|
50529
|
-
hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
52255
|
+
) : /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
52256
|
+
/* @__PURE__ */ jsxRuntime.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` }),
|
|
52257
|
+
(currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "opacity-90 font-mono bg-black/30 px-2 py-0.5 rounded", children: [
|
|
52258
|
+
"Cycle time: ",
|
|
52259
|
+
currentVideo.cycle_time_seconds.toFixed(1),
|
|
52260
|
+
"s"
|
|
52261
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
52262
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
|
|
52263
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
|
|
52264
|
+
] })
|
|
52265
|
+
] }) }) : (
|
|
52266
|
+
/* Right side display for other video types */
|
|
52267
|
+
currentVideo.type === "idle_time" ? (
|
|
52268
|
+
// Show full colored badge for idle time
|
|
52269
|
+
(() => {
|
|
52270
|
+
const classification = getIdleTimeClassification(currentVideo);
|
|
52271
|
+
const confidence = getIdleTimeConfidence(currentVideo);
|
|
52272
|
+
const config = getRootCauseConfig(classification);
|
|
52273
|
+
if (!config) return null;
|
|
52274
|
+
const IconComponent = config.Icon;
|
|
52275
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
52276
|
+
idleTimeVlmEnabled && confidence !== null && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("span", { className: "font-medium text-xs text-white", children: [
|
|
52277
|
+
"AI Confidence: ",
|
|
52278
|
+
(confidence * 100).toFixed(0),
|
|
52279
|
+
"%"
|
|
52280
|
+
] }) }) }),
|
|
52281
|
+
idleTimeVlmEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 right-3 z-10", children: /* @__PURE__ */ jsxRuntime.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: [
|
|
52282
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: `h-3.5 w-3.5 flex-shrink-0 ${config.iconColor}`, strokeWidth: 2.5 }),
|
|
52283
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `truncate font-medium text-xs ${config.color}`, children: config.displayName || "Analyzing..." })
|
|
52284
|
+
] }) })
|
|
52285
|
+
] });
|
|
52286
|
+
})()
|
|
52287
|
+
) : /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
52288
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${getSeverityColor(currentVideo.severity)} mr-2 animate-pulse` }),
|
|
52289
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
|
|
52290
|
+
currentVideo.type !== "recent_flow_red_streak" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
|
|
52291
|
+
] }) })
|
|
52292
|
+
)
|
|
52293
|
+
] }) }) }) : shouldHoldInitialTimeHandoffVideo ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative lg:h-full", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading clips..." }) }) }) }) : (
|
|
52294
|
+
/* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
|
|
52295
|
+
hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
50530
52296
|
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
50531
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No
|
|
50532
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "There
|
|
52297
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
|
|
52298
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "There were no video clips found for this workspace today." })
|
|
50533
52299
|
] }) }) : (
|
|
50534
|
-
/* Priority
|
|
50535
|
-
|
|
52300
|
+
/* Priority 5.5: Show "no folder selected" if activeFilter is empty */
|
|
52301
|
+
hasInitialLoad && !activeFilter ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
52302
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
52303
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Folder Selected" }),
|
|
52304
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "Please select a folder to view clips." })
|
|
52305
|
+
] }) }) : (
|
|
52306
|
+
/* Priority 6: Show "no matching clips" only if we have data loaded and specifically no clips for this filter */
|
|
52307
|
+
hasInitialLoad && (mergedCounts[activeFilter] || 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[220px] sm:min-h-[320px] lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
52308
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
52309
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Matching Clips" }),
|
|
52310
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500", children: "There are no clips matching the selected filter." })
|
|
52311
|
+
] }) }) : (
|
|
52312
|
+
/* Priority 7: Default loading state for any other case */
|
|
52313
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 lg:h-full", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative lg:h-full", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading..." }) }) }) })
|
|
52314
|
+
)
|
|
52315
|
+
)
|
|
50536
52316
|
)
|
|
50537
|
-
)
|
|
50538
|
-
|
|
50539
|
-
)
|
|
52317
|
+
) })
|
|
52318
|
+
}
|
|
52319
|
+
),
|
|
50540
52320
|
/* @__PURE__ */ jsxRuntime.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 ? (
|
|
50541
52321
|
/* Triage Mode - Direct tile view for cycle completions and idle time */
|
|
50542
52322
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-white rounded-lg shadow-sm h-full overflow-hidden flex flex-col", children: [
|
|
@@ -50671,7 +52451,7 @@ var BottlenecksContent = ({
|
|
|
50671
52451
|
})),
|
|
50672
52452
|
videos: allVideos || [],
|
|
50673
52453
|
activeFilter,
|
|
50674
|
-
currentVideoId: currentVideo?.id,
|
|
52454
|
+
currentVideoId: currentVideo?.id || currentClipId || void 0,
|
|
50675
52455
|
counts: mergedCounts,
|
|
50676
52456
|
isReady: hasInitialLoad,
|
|
50677
52457
|
prefetchedPercentileCounts: isFastSlowClipFiltersEnabled ? prefetchedPercentileCounts || void 0 : void 0,
|
|
@@ -50685,11 +52465,9 @@ var BottlenecksContent = ({
|
|
|
50685
52465
|
idleTimeVlmEnabled,
|
|
50686
52466
|
showPercentileCycleFilters: isFastSlowClipFiltersEnabled,
|
|
50687
52467
|
prefetchedClipMetadata: prefetchedExplorerMetadata,
|
|
50688
|
-
|
|
50689
|
-
|
|
50690
|
-
|
|
50691
|
-
)
|
|
50692
|
-
},
|
|
52468
|
+
prefetchedClipTotals: initialTimePrefetch?.loading ? void 0 : initialTimePrefetch?.totalsByCategory,
|
|
52469
|
+
prefetchedPercentileClips: initialTimePrefetch?.loading ? void 0 : initialTimePrefetch?.percentileClipsByCategory,
|
|
52470
|
+
externallyManagedLoadingCategories,
|
|
50693
52471
|
activeCategoryLoading: isCategoryLoading,
|
|
50694
52472
|
idleClipSort,
|
|
50695
52473
|
onIdleClipSortChange: setIdleClipSort,
|
|
@@ -70503,9 +72281,7 @@ function withWorkspaceDisplayNames(Component3, options = {}) {
|
|
|
70503
72281
|
await preInitializeWorkspaceDisplayNames(selectedLineId);
|
|
70504
72282
|
}
|
|
70505
72283
|
} else if (lineIdArray.length > 0) {
|
|
70506
|
-
await
|
|
70507
|
-
lineIdArray.map((lineId) => preInitializeWorkspaceDisplayNames(lineId))
|
|
70508
|
-
);
|
|
72284
|
+
await preInitializeWorkspaceDisplayNamesForLines(lineIdArray);
|
|
70509
72285
|
} else {
|
|
70510
72286
|
await preInitializeWorkspaceDisplayNames();
|
|
70511
72287
|
}
|
|
@@ -80969,7 +82745,6 @@ var TargetsViewUI = ({
|
|
|
80969
82745
|
skuRequired = false,
|
|
80970
82746
|
onUpdateWorkspaceSelectedSku
|
|
80971
82747
|
}) => {
|
|
80972
|
-
const { displayNames: workspaceDisplayNames } = useWorkspaceDisplayNames();
|
|
80973
82748
|
const mobileMenuContext = useMobileMenu();
|
|
80974
82749
|
useHideMobileHeader(!!mobileMenuContext);
|
|
80975
82750
|
if (isLoading) {
|
|
@@ -81151,7 +82926,7 @@ var TargetsViewUI = ({
|
|
|
81151
82926
|
] })
|
|
81152
82927
|
] }) }),
|
|
81153
82928
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-gray-100", children: line.workspaces.map((workspace) => {
|
|
81154
|
-
const formattedName =
|
|
82929
|
+
const formattedName = workspace.displayName || formatWorkspaceName(workspace.name, lineId);
|
|
81155
82930
|
const realSkuOptions = (workspace.skuRows || []).filter((r2) => !r2.is_dummy);
|
|
81156
82931
|
const showSkuDropdown = !!onUpdateWorkspaceSelectedSku && realSkuOptions.length >= 1;
|
|
81157
82932
|
const isPlanLocked = !!workspace.productionPlanLock?.locked;
|
|
@@ -81702,6 +83477,7 @@ var TargetsView = ({
|
|
|
81702
83477
|
return {
|
|
81703
83478
|
id: ws.id,
|
|
81704
83479
|
name: ws.workspace_id,
|
|
83480
|
+
displayName: ws.display_name || ws.workspace_id,
|
|
81705
83481
|
targetPPH: selectedRow?.pph_threshold ?? (threshold?.pph_threshold ? Math.round(threshold.pph_threshold) : ""),
|
|
81706
83482
|
targetCycleTime: selectedRow?.ideal_cycle_time ?? (threshold?.ideal_cycle_time ?? ""),
|
|
81707
83483
|
targetDayOutput: selectedRow?.total_day_output ?? (threshold?.total_day_output ?? ""),
|
|
@@ -81760,6 +83536,7 @@ var TargetsView = ({
|
|
|
81760
83536
|
return {
|
|
81761
83537
|
id: ws.id,
|
|
81762
83538
|
name: ws.workspace_id,
|
|
83539
|
+
displayName: ws.display_name || ws.workspace_id,
|
|
81763
83540
|
targetPPH: "",
|
|
81764
83541
|
targetCycleTime: "",
|
|
81765
83542
|
targetDayOutput: "",
|
|
@@ -82240,15 +84017,31 @@ var TargetsView = ({
|
|
|
82240
84017
|
const handleUpdateWorkspaceDisplayName = React125.useCallback(async (workspaceId, displayName) => {
|
|
82241
84018
|
try {
|
|
82242
84019
|
const updated = await workspaceService.updateWorkspaceDisplayName(workspaceId, displayName);
|
|
84020
|
+
const nextDisplayName = updated?.display_name || displayName;
|
|
84021
|
+
setAllShiftsData((prev) => {
|
|
84022
|
+
const next = { ...prev };
|
|
84023
|
+
Object.entries(prev).forEach(([shiftId, shiftData]) => {
|
|
84024
|
+
const numericShiftId = Number(shiftId);
|
|
84025
|
+
const updatedShiftData = {};
|
|
84026
|
+
Object.entries(shiftData).forEach(([lineId, line]) => {
|
|
84027
|
+
updatedShiftData[lineId] = {
|
|
84028
|
+
...line,
|
|
84029
|
+
workspaces: line.workspaces.map(
|
|
84030
|
+
(ws) => ws.id === workspaceId ? { ...ws, displayName: nextDisplayName } : ws
|
|
84031
|
+
)
|
|
84032
|
+
};
|
|
84033
|
+
});
|
|
84034
|
+
next[numericShiftId] = updatedShiftData;
|
|
84035
|
+
});
|
|
84036
|
+
return next;
|
|
84037
|
+
});
|
|
82243
84038
|
if (updated?.line_id && updated?.workspace_id) {
|
|
82244
84039
|
upsertWorkspaceDisplayNameInCache({
|
|
82245
84040
|
lineId: updated.line_id,
|
|
82246
84041
|
workspaceId: updated.workspace_id,
|
|
82247
|
-
displayName:
|
|
84042
|
+
displayName: nextDisplayName,
|
|
82248
84043
|
enabled: updated?.enable
|
|
82249
84044
|
});
|
|
82250
|
-
} else {
|
|
82251
|
-
await forceRefreshWorkspaceDisplayNames();
|
|
82252
84045
|
}
|
|
82253
84046
|
sonner.toast.success("Workspace name updated successfully");
|
|
82254
84047
|
} catch (error) {
|
|
@@ -82292,9 +84085,8 @@ var TargetsView = ({
|
|
|
82292
84085
|
}
|
|
82293
84086
|
);
|
|
82294
84087
|
};
|
|
82295
|
-
var
|
|
82296
|
-
var
|
|
82297
|
-
var AuthenticatedTargetsView = withAuth(React125__namespace.default.memo(TargetsViewWithDisplayNames));
|
|
84088
|
+
var TargetsView_default = TargetsView;
|
|
84089
|
+
var AuthenticatedTargetsView = withAuth(React125__namespace.default.memo(TargetsView));
|
|
82298
84090
|
function useTimezone(options = {}) {
|
|
82299
84091
|
const dashboardConfig = useDashboardConfig();
|
|
82300
84092
|
const workspaceConfig = useWorkspaceConfig();
|
|
@@ -82474,17 +84266,22 @@ var WorkspaceHourSummaryPanel = ({
|
|
|
82474
84266
|
return;
|
|
82475
84267
|
}
|
|
82476
84268
|
autoSummaryKeyRef.current = requestKey;
|
|
82477
|
-
|
|
82478
|
-
|
|
82479
|
-
|
|
82480
|
-
|
|
82481
|
-
|
|
82482
|
-
|
|
82483
|
-
|
|
82484
|
-
|
|
82485
|
-
|
|
82486
|
-
|
|
82487
|
-
|
|
84269
|
+
const summaryTimer = window.setTimeout(() => {
|
|
84270
|
+
void summarize({
|
|
84271
|
+
workspaceId,
|
|
84272
|
+
companyId,
|
|
84273
|
+
date,
|
|
84274
|
+
shiftId,
|
|
84275
|
+
hourIndex: selectedHour.hourIndex,
|
|
84276
|
+
hourStart: selectedHour.startTime,
|
|
84277
|
+
hourEnd: selectedHour.endTime,
|
|
84278
|
+
forceRefresh: false,
|
|
84279
|
+
selectionSource: selectedHour.source ?? "unknown"
|
|
84280
|
+
}).catch(() => void 0);
|
|
84281
|
+
}, 1500);
|
|
84282
|
+
return () => {
|
|
84283
|
+
window.clearTimeout(summaryTimer);
|
|
84284
|
+
};
|
|
82488
84285
|
}, [canSummarize, companyId, date, selectedHour, selectedKey, shiftId, summarize, workspaceId]);
|
|
82489
84286
|
if (!selectedHour) {
|
|
82490
84287
|
return null;
|
|
@@ -82668,10 +84465,18 @@ var WorkspaceDetailView = ({
|
|
|
82668
84465
|
setSelectedHour(null);
|
|
82669
84466
|
}, [workspaceId, date, shift]);
|
|
82670
84467
|
const dashboardConfig = useDashboardConfig();
|
|
84468
|
+
const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
|
|
82671
84469
|
const { legend: efficiencyLegend } = useEfficiencyLegend();
|
|
82672
84470
|
const prewarmedClipsRef = React125.useRef(/* @__PURE__ */ new Set());
|
|
82673
84471
|
const prewarmInFlightRef = React125.useRef(/* @__PURE__ */ new Set());
|
|
82674
84472
|
const [lowMomentsPrefetch, setLowMomentsPrefetch] = React125.useState(null);
|
|
84473
|
+
const selectedHourClipPrefetchInFlightRef = React125.useRef(/* @__PURE__ */ new Set());
|
|
84474
|
+
const selectedHourClipPrefetchAbortRef = React125.useRef(null);
|
|
84475
|
+
const [initialTimePrefetch, setInitialTimePrefetch] = React125.useState(null);
|
|
84476
|
+
const {
|
|
84477
|
+
isFastSlowClipFiltersEnabled,
|
|
84478
|
+
isResolved: isFastSlowClipFiltersResolved
|
|
84479
|
+
} = useCompanyFastSlowClipFiltersEnabled();
|
|
82675
84480
|
const [aiSummaryHour, setAiSummaryHour] = React125.useState(null);
|
|
82676
84481
|
const buildHourlyOutputActionTrackingProps = React125.useCallback((payload) => ({
|
|
82677
84482
|
workspace_id: workspaceId,
|
|
@@ -82701,24 +84506,286 @@ var WorkspaceDetailView = ({
|
|
|
82701
84506
|
timezone,
|
|
82702
84507
|
workspaceId
|
|
82703
84508
|
]);
|
|
84509
|
+
const prefetchClipsForSelectedHour = React125.useCallback((hour) => {
|
|
84510
|
+
if (!isClipsEnabled || !dashboardConfig?.s3Config || !workspaceId || !supabase) {
|
|
84511
|
+
return;
|
|
84512
|
+
}
|
|
84513
|
+
const resolvedDate = date || getOperationalDate(timezone);
|
|
84514
|
+
const resolvedShiftId = parsedShiftId ?? selectedShift;
|
|
84515
|
+
if (!resolvedDate || resolvedShiftId === null || resolvedShiftId === void 0) {
|
|
84516
|
+
return;
|
|
84517
|
+
}
|
|
84518
|
+
const categoryId = "cycle_completion";
|
|
84519
|
+
const regularCategoryIds = ["cycle_completion", "idle_time", "recent_flow_red_streak"];
|
|
84520
|
+
const percentileCategoryIds = isFastSlowClipFiltersEnabled ? ["fast-cycles", "slow-cycles"] : [];
|
|
84521
|
+
const allPrefetchCategoryIds = [...regularCategoryIds, ...percentileCategoryIds];
|
|
84522
|
+
const effectiveTimezone = hour.timezone || timezone;
|
|
84523
|
+
const prefetchKey = [
|
|
84524
|
+
workspaceId,
|
|
84525
|
+
resolvedDate,
|
|
84526
|
+
resolvedShiftId,
|
|
84527
|
+
hour.startTime,
|
|
84528
|
+
hour.endTime,
|
|
84529
|
+
effectiveTimezone || "",
|
|
84530
|
+
allPrefetchCategoryIds.join(",")
|
|
84531
|
+
].join("|");
|
|
84532
|
+
if (initialTimePrefetch?.key === prefetchKey && !initialTimePrefetch.loading) {
|
|
84533
|
+
return;
|
|
84534
|
+
}
|
|
84535
|
+
if (selectedHourClipPrefetchInFlightRef.current.has(prefetchKey)) {
|
|
84536
|
+
return;
|
|
84537
|
+
}
|
|
84538
|
+
selectedHourClipPrefetchAbortRef.current?.abort();
|
|
84539
|
+
const controller = new AbortController();
|
|
84540
|
+
selectedHourClipPrefetchAbortRef.current = controller;
|
|
84541
|
+
selectedHourClipPrefetchInFlightRef.current.add(prefetchKey);
|
|
84542
|
+
const existingSnapshot = initialTimePrefetch?.key === prefetchKey ? initialTimePrefetch : null;
|
|
84543
|
+
setInitialTimePrefetch({
|
|
84544
|
+
key: prefetchKey,
|
|
84545
|
+
categoryId,
|
|
84546
|
+
metadata: existingSnapshot?.metadata || [],
|
|
84547
|
+
metadataByCategory: existingSnapshot?.metadataByCategory || {},
|
|
84548
|
+
totalsByCategory: existingSnapshot?.totalsByCategory || {},
|
|
84549
|
+
percentileClipsByCategory: existingSnapshot?.percentileClipsByCategory || {},
|
|
84550
|
+
firstVideo: existingSnapshot?.firstVideo || null,
|
|
84551
|
+
total: existingSnapshot?.total || 0,
|
|
84552
|
+
loading: true,
|
|
84553
|
+
error: null
|
|
84554
|
+
});
|
|
84555
|
+
const s3Service = videoPrefetchManager.getS3Service(dashboardConfig);
|
|
84556
|
+
const fetchHourlySnapshot = async () => {
|
|
84557
|
+
try {
|
|
84558
|
+
const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
|
|
84559
|
+
method: "POST",
|
|
84560
|
+
headers: {
|
|
84561
|
+
"Content-Type": "application/json"
|
|
84562
|
+
},
|
|
84563
|
+
body: JSON.stringify({
|
|
84564
|
+
action: "hourly-snapshot",
|
|
84565
|
+
workspaceId,
|
|
84566
|
+
date: resolvedDate,
|
|
84567
|
+
shift: resolvedShiftId,
|
|
84568
|
+
startTime: hour.startTime,
|
|
84569
|
+
endTime: hour.endTime,
|
|
84570
|
+
timeFilterTimezone: effectiveTimezone
|
|
84571
|
+
}),
|
|
84572
|
+
signal: controller.signal,
|
|
84573
|
+
redirectReason: "session_expired"
|
|
84574
|
+
});
|
|
84575
|
+
if (!response.ok) {
|
|
84576
|
+
return false;
|
|
84577
|
+
}
|
|
84578
|
+
const snapshot = await response.json();
|
|
84579
|
+
const metadataByCategory = snapshot?.metadataByCategory && typeof snapshot.metadataByCategory === "object" ? snapshot.metadataByCategory : {};
|
|
84580
|
+
const percentileClipsByCategory = snapshot?.percentileClipsByCategory && typeof snapshot.percentileClipsByCategory === "object" ? snapshot.percentileClipsByCategory : {};
|
|
84581
|
+
const totalsByCategory = snapshot?.totalsByCategory && typeof snapshot.totalsByCategory === "object" ? Object.entries(snapshot.totalsByCategory).reduce((accumulator, [key, value]) => {
|
|
84582
|
+
const total = Number(value);
|
|
84583
|
+
accumulator[key] = Number.isFinite(total) ? Math.max(0, total) : 0;
|
|
84584
|
+
return accumulator;
|
|
84585
|
+
}, {}) : {};
|
|
84586
|
+
const snapshotCategoryId = typeof snapshot?.categoryId === "string" ? snapshot.categoryId : categoryId;
|
|
84587
|
+
const metadata = Array.isArray(snapshot?.metadata) ? snapshot.metadata : Array.isArray(metadataByCategory[snapshotCategoryId]) ? metadataByCategory[snapshotCategoryId] : [];
|
|
84588
|
+
if (controller.signal.aborted) {
|
|
84589
|
+
return true;
|
|
84590
|
+
}
|
|
84591
|
+
setInitialTimePrefetch({
|
|
84592
|
+
key: prefetchKey,
|
|
84593
|
+
categoryId: snapshotCategoryId,
|
|
84594
|
+
metadata,
|
|
84595
|
+
metadataByCategory,
|
|
84596
|
+
totalsByCategory,
|
|
84597
|
+
percentileClipsByCategory,
|
|
84598
|
+
firstVideo: snapshot?.firstVideo || null,
|
|
84599
|
+
total: typeof totalsByCategory[snapshotCategoryId] === "number" ? totalsByCategory[snapshotCategoryId] : metadata.length,
|
|
84600
|
+
loading: false,
|
|
84601
|
+
error: null
|
|
84602
|
+
});
|
|
84603
|
+
return true;
|
|
84604
|
+
} catch (error2) {
|
|
84605
|
+
if (error2.name === "AbortError") {
|
|
84606
|
+
return true;
|
|
84607
|
+
}
|
|
84608
|
+
console.warn("[WorkspaceDetailView] Hourly clips snapshot failed; falling back to category prefetch:", error2);
|
|
84609
|
+
return false;
|
|
84610
|
+
}
|
|
84611
|
+
};
|
|
84612
|
+
const fetchRegularCategory = async (prefetchCategoryId) => {
|
|
84613
|
+
const metadataResponse = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
|
|
84614
|
+
method: "POST",
|
|
84615
|
+
headers: {
|
|
84616
|
+
"Content-Type": "application/json"
|
|
84617
|
+
},
|
|
84618
|
+
body: JSON.stringify({
|
|
84619
|
+
action: "clip-metadata",
|
|
84620
|
+
workspaceId,
|
|
84621
|
+
date: resolvedDate,
|
|
84622
|
+
shift: resolvedShiftId,
|
|
84623
|
+
category: prefetchCategoryId,
|
|
84624
|
+
page: 1,
|
|
84625
|
+
limit: 500,
|
|
84626
|
+
knownTotal: null,
|
|
84627
|
+
startTime: hour.startTime,
|
|
84628
|
+
endTime: hour.endTime,
|
|
84629
|
+
timeFilterTimezone: effectiveTimezone,
|
|
84630
|
+
sort: prefetchCategoryId === "recent_flow_red_streak" ? "red_flow_output_shortfall_desc" : "latest"
|
|
84631
|
+
}),
|
|
84632
|
+
signal: controller.signal,
|
|
84633
|
+
redirectReason: "session_expired"
|
|
84634
|
+
});
|
|
84635
|
+
if (!metadataResponse.ok) {
|
|
84636
|
+
throw new Error(`Hourly clips metadata prefetch failed for ${prefetchCategoryId}: ${metadataResponse.status}`);
|
|
84637
|
+
}
|
|
84638
|
+
const metadataData = await metadataResponse.json();
|
|
84639
|
+
return {
|
|
84640
|
+
categoryId: prefetchCategoryId,
|
|
84641
|
+
clips: Array.isArray(metadataData?.clips) ? metadataData.clips : [],
|
|
84642
|
+
total: typeof metadataData?.total === "number" ? metadataData.total : 0
|
|
84643
|
+
};
|
|
84644
|
+
};
|
|
84645
|
+
const fetchPercentileCategory = async (prefetchCategoryId) => {
|
|
84646
|
+
const response = await fetchWithSupabaseAuth(supabase, "/api/clips/supabase", {
|
|
84647
|
+
method: "POST",
|
|
84648
|
+
headers: {
|
|
84649
|
+
"Content-Type": "application/json"
|
|
84650
|
+
},
|
|
84651
|
+
body: JSON.stringify({
|
|
84652
|
+
action: "percentile-clips",
|
|
84653
|
+
workspaceId,
|
|
84654
|
+
startDate: `${resolvedDate}T00:00:00Z`,
|
|
84655
|
+
endDate: `${resolvedDate}T23:59:59Z`,
|
|
84656
|
+
percentile: 10,
|
|
84657
|
+
shiftId: resolvedShiftId,
|
|
84658
|
+
limit: 500,
|
|
84659
|
+
startTime: hour.startTime,
|
|
84660
|
+
endTime: hour.endTime,
|
|
84661
|
+
timeFilterTimezone: effectiveTimezone,
|
|
84662
|
+
percentileAction: prefetchCategoryId
|
|
84663
|
+
}),
|
|
84664
|
+
signal: controller.signal,
|
|
84665
|
+
redirectReason: "session_expired"
|
|
84666
|
+
});
|
|
84667
|
+
if (!response.ok) {
|
|
84668
|
+
throw new Error(`Hourly percentile clips prefetch failed for ${prefetchCategoryId}: ${response.status}`);
|
|
84669
|
+
}
|
|
84670
|
+
const data = await response.json();
|
|
84671
|
+
return {
|
|
84672
|
+
categoryId: prefetchCategoryId,
|
|
84673
|
+
clips: Array.isArray(data?.clips) ? data.clips : [],
|
|
84674
|
+
total: typeof data?.total === "number" ? data.total : 0
|
|
84675
|
+
};
|
|
84676
|
+
};
|
|
84677
|
+
const runPrefetch = async () => {
|
|
84678
|
+
try {
|
|
84679
|
+
const snapshotLoaded = await fetchHourlySnapshot();
|
|
84680
|
+
if (snapshotLoaded) {
|
|
84681
|
+
return;
|
|
84682
|
+
}
|
|
84683
|
+
const cycleCompletionPromise = fetchRegularCategory("cycle_completion");
|
|
84684
|
+
let resolvedFirstVideo = null;
|
|
84685
|
+
const firstVideoPromise = cycleCompletionPromise.then(async (cycleResponse) => {
|
|
84686
|
+
const firstClipId = cycleResponse.clips[0]?.clipId || cycleResponse.clips[0]?.id || null;
|
|
84687
|
+
if (!firstClipId) {
|
|
84688
|
+
return null;
|
|
84689
|
+
}
|
|
84690
|
+
const video = await s3Service.getClipById(firstClipId);
|
|
84691
|
+
resolvedFirstVideo = video;
|
|
84692
|
+
if (!controller.signal.aborted) {
|
|
84693
|
+
setInitialTimePrefetch((prev) => prev?.key === prefetchKey ? {
|
|
84694
|
+
...prev,
|
|
84695
|
+
firstVideo: video
|
|
84696
|
+
} : prev);
|
|
84697
|
+
}
|
|
84698
|
+
return video;
|
|
84699
|
+
}).catch((error2) => {
|
|
84700
|
+
if (error2.name !== "AbortError") {
|
|
84701
|
+
console.warn("[WorkspaceDetailView] Hourly first clip prefetch failed:", error2);
|
|
84702
|
+
}
|
|
84703
|
+
return null;
|
|
84704
|
+
});
|
|
84705
|
+
const percentileResponsesPromise = Promise.all(percentileCategoryIds.map(fetchPercentileCategory));
|
|
84706
|
+
const regularResponses = await Promise.all([
|
|
84707
|
+
cycleCompletionPromise,
|
|
84708
|
+
fetchRegularCategory("idle_time"),
|
|
84709
|
+
fetchRegularCategory("recent_flow_red_streak")
|
|
84710
|
+
]);
|
|
84711
|
+
const percentileResponses = await percentileResponsesPromise;
|
|
84712
|
+
const metadataByCategory = regularResponses.reduce((accumulator, response) => {
|
|
84713
|
+
accumulator[response.categoryId] = response.clips;
|
|
84714
|
+
return accumulator;
|
|
84715
|
+
}, {});
|
|
84716
|
+
const percentileClipsByCategory = percentileResponses.reduce((accumulator, response) => {
|
|
84717
|
+
accumulator[response.categoryId] = response.clips;
|
|
84718
|
+
return accumulator;
|
|
84719
|
+
}, {});
|
|
84720
|
+
const totalsByCategory = [...regularResponses, ...percentileResponses].reduce((accumulator, response) => {
|
|
84721
|
+
accumulator[response.categoryId] = Math.max(0, Number(response.total || 0));
|
|
84722
|
+
return accumulator;
|
|
84723
|
+
}, {});
|
|
84724
|
+
const metadata = metadataByCategory[categoryId] || [];
|
|
84725
|
+
if (controller.signal.aborted) {
|
|
84726
|
+
return;
|
|
84727
|
+
}
|
|
84728
|
+
setInitialTimePrefetch({
|
|
84729
|
+
key: prefetchKey,
|
|
84730
|
+
categoryId,
|
|
84731
|
+
metadata,
|
|
84732
|
+
metadataByCategory,
|
|
84733
|
+
totalsByCategory,
|
|
84734
|
+
percentileClipsByCategory,
|
|
84735
|
+
firstVideo: resolvedFirstVideo,
|
|
84736
|
+
total: typeof totalsByCategory[categoryId] === "number" ? totalsByCategory[categoryId] : metadata.length,
|
|
84737
|
+
loading: false,
|
|
84738
|
+
error: null
|
|
84739
|
+
});
|
|
84740
|
+
void firstVideoPromise;
|
|
84741
|
+
} catch (error2) {
|
|
84742
|
+
if (error2.name === "AbortError") {
|
|
84743
|
+
return;
|
|
84744
|
+
}
|
|
84745
|
+
console.warn("[WorkspaceDetailView] Hourly clips prefetch failed:", error2);
|
|
84746
|
+
setInitialTimePrefetch((prev) => prev?.key === prefetchKey ? {
|
|
84747
|
+
...prev,
|
|
84748
|
+
loading: false,
|
|
84749
|
+
error: error2 instanceof Error ? error2.message : "Hourly clips prefetch failed"
|
|
84750
|
+
} : prev);
|
|
84751
|
+
} finally {
|
|
84752
|
+
selectedHourClipPrefetchInFlightRef.current.delete(prefetchKey);
|
|
84753
|
+
}
|
|
84754
|
+
};
|
|
84755
|
+
void runPrefetch();
|
|
84756
|
+
}, [
|
|
84757
|
+
dashboardConfig,
|
|
84758
|
+
date,
|
|
84759
|
+
initialTimePrefetch,
|
|
84760
|
+
isFastSlowClipFiltersEnabled,
|
|
84761
|
+
isClipsEnabled,
|
|
84762
|
+
parsedShiftId,
|
|
84763
|
+
selectedShift,
|
|
84764
|
+
supabase,
|
|
84765
|
+
timezone,
|
|
84766
|
+
workspaceId
|
|
84767
|
+
]);
|
|
82704
84768
|
const handleOutputHourSelect = React125.useCallback((payload) => {
|
|
82705
|
-
|
|
84769
|
+
const hour = {
|
|
82706
84770
|
source: "output",
|
|
82707
84771
|
hourIndex: payload.hourIndex,
|
|
82708
84772
|
timeRange: payload.timeRange,
|
|
82709
84773
|
startTime: payload.startTime,
|
|
82710
84774
|
endTime: payload.endTime,
|
|
84775
|
+
timezone: payload.timezone,
|
|
82711
84776
|
status: payload.status,
|
|
82712
84777
|
output: payload.output,
|
|
82713
84778
|
target: payload.target
|
|
82714
|
-
}
|
|
82715
|
-
|
|
84779
|
+
};
|
|
84780
|
+
setSelectedHour(hour);
|
|
84781
|
+
prefetchClipsForSelectedHour(hour);
|
|
84782
|
+
}, [prefetchClipsForSelectedHour]);
|
|
82716
84783
|
const handleAiSummaryClick = React125.useCallback((payload) => {
|
|
82717
84784
|
trackCoreEvent("Hourly Output Ask AI Clicked", buildHourlyOutputActionTrackingProps(payload));
|
|
82718
84785
|
setAiSummaryHour(payload);
|
|
82719
84786
|
}, [buildHourlyOutputActionTrackingProps]);
|
|
82720
84787
|
const handleCycleHourSelect = React125.useCallback((payload) => {
|
|
82721
|
-
|
|
84788
|
+
const hour = {
|
|
82722
84789
|
source: "cycle",
|
|
82723
84790
|
hourIndex: payload.hourIndex,
|
|
82724
84791
|
timeRange: payload.timeRange,
|
|
@@ -82728,17 +84795,23 @@ var WorkspaceDetailView = ({
|
|
|
82728
84795
|
cycleTime: payload.cycleTime,
|
|
82729
84796
|
idealCycleTime: payload.idealCycleTime,
|
|
82730
84797
|
idleMinutes: payload.idleMinutes
|
|
82731
|
-
}
|
|
82732
|
-
|
|
84798
|
+
};
|
|
84799
|
+
setSelectedHour(hour);
|
|
84800
|
+
prefetchClipsForSelectedHour(hour);
|
|
84801
|
+
}, [prefetchClipsForSelectedHour]);
|
|
82733
84802
|
const handleOpenClipsForHour = React125.useCallback((hour) => {
|
|
84803
|
+
prefetchClipsForSelectedHour(hour);
|
|
82734
84804
|
setPendingClipHourFilter({
|
|
82735
84805
|
startTime: hour.startTime,
|
|
82736
84806
|
endTime: hour.endTime,
|
|
82737
84807
|
sourceLabel: hour.timeRange,
|
|
82738
|
-
status: hour.status === "below_target" || hour.status === "above_standard" ? "below_target" : "met_target"
|
|
84808
|
+
status: hour.status === "below_target" || hour.status === "above_standard" ? "below_target" : "met_target",
|
|
84809
|
+
timezone: hour.timezone,
|
|
84810
|
+
categoryId: "cycle_completion",
|
|
84811
|
+
categoryIds: ["cycle_completion", "fast-cycles", "slow-cycles", "idle_time", "recent_flow_red_streak"]
|
|
82739
84812
|
});
|
|
82740
84813
|
setActiveTab("bottlenecks");
|
|
82741
|
-
}, []);
|
|
84814
|
+
}, [prefetchClipsForSelectedHour]);
|
|
82742
84815
|
const handleWatchClipsFromChart = React125.useCallback((payload) => {
|
|
82743
84816
|
trackCoreEvent("Hourly Output Watch Clips Clicked", buildHourlyOutputActionTrackingProps(payload));
|
|
82744
84817
|
handleOpenClipsForHour({
|
|
@@ -82747,6 +84820,7 @@ var WorkspaceDetailView = ({
|
|
|
82747
84820
|
timeRange: payload.timeRange,
|
|
82748
84821
|
startTime: payload.startTime,
|
|
82749
84822
|
endTime: payload.endTime,
|
|
84823
|
+
timezone: payload.timezone,
|
|
82750
84824
|
status: payload.status,
|
|
82751
84825
|
output: payload.output,
|
|
82752
84826
|
target: payload.target
|
|
@@ -82769,11 +84843,6 @@ var WorkspaceDetailView = ({
|
|
|
82769
84843
|
startDate: isFullRange ? void 0 : rangeStart,
|
|
82770
84844
|
endDate: isFullRange ? void 0 : rangeEnd
|
|
82771
84845
|
});
|
|
82772
|
-
const {
|
|
82773
|
-
isFastSlowClipFiltersEnabled,
|
|
82774
|
-
isResolved: isFastSlowClipFiltersResolved
|
|
82775
|
-
} = useCompanyFastSlowClipFiltersEnabled();
|
|
82776
|
-
const isClipsEnabled = dashboardConfig?.clipsConfig?.enabled ?? true;
|
|
82777
84846
|
dashboardConfig?.supervisorConfig?.enabled || false;
|
|
82778
84847
|
const routedLineId = lineId || selectedLineId;
|
|
82779
84848
|
const latestCachedDetailedMetrics = React125.useMemo(() => {
|
|
@@ -84520,6 +86589,7 @@ var WorkspaceDetailView = ({
|
|
|
84520
86589
|
workspaceMetrics: detailedWorkspaceMetrics || void 0,
|
|
84521
86590
|
prefetchedPercentileCounts: isFastSlowClipFiltersEnabled ? prefetchedPercentileCounts : null,
|
|
84522
86591
|
lowMomentsPrefetch,
|
|
86592
|
+
initialTimePrefetch,
|
|
84523
86593
|
initialTimeFilter: pendingClipHourFilter,
|
|
84524
86594
|
className: "h-[calc(100vh-10rem)]"
|
|
84525
86595
|
}
|
|
@@ -94507,6 +96577,7 @@ exports.parseDateKeyToDate = parseDateKeyToDate;
|
|
|
94507
96577
|
exports.parseS3Uri = parseS3Uri;
|
|
94508
96578
|
exports.pickPreferredLineMetricsRow = pickPreferredLineMetricsRow;
|
|
94509
96579
|
exports.preInitializeWorkspaceDisplayNames = preInitializeWorkspaceDisplayNames;
|
|
96580
|
+
exports.preInitializeWorkspaceDisplayNamesForLines = preInitializeWorkspaceDisplayNamesForLines;
|
|
94510
96581
|
exports.preloadS3Video = preloadS3Video;
|
|
94511
96582
|
exports.preloadS3VideoUrl = preloadS3VideoUrl;
|
|
94512
96583
|
exports.preloadS3VideosUrl = preloadS3VideosUrl;
|
|
@@ -94657,3 +96728,5 @@ exports.withRegistry = withRegistry;
|
|
|
94657
96728
|
exports.withTimezone = withTimezone;
|
|
94658
96729
|
exports.workspaceHealthService = workspaceHealthService;
|
|
94659
96730
|
exports.workspaceService = workspaceService;
|
|
96731
|
+
//# sourceMappingURL=index.js.map
|
|
96732
|
+
//# sourceMappingURL=index.js.map
|