@optifye/dashboard-core 6.6.5 → 6.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +154 -6
- package/dist/index.d.mts +223 -250
- package/dist/index.d.ts +223 -250
- package/dist/index.js +2502 -2112
- package/dist/index.mjs +1605 -1221
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import
|
|
1
|
+
import * as React20 from 'react';
|
|
2
|
+
import React20__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, memo, forwardRef, useImperativeHandle, useContext, useLayoutEffect, useId, Children, isValidElement, useInsertionEffect, Fragment as Fragment$1, createElement, Component } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import { useRouter } from 'next/router';
|
|
5
5
|
import { toZonedTime, formatInTimeZone } from 'date-fns-tz';
|
|
@@ -13,7 +13,7 @@ import { noop, warning, invariant, progress, secondsToMilliseconds, milliseconds
|
|
|
13
13
|
import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
|
|
14
14
|
import { BarChart as BarChart$1, CartesianGrid, XAxis, YAxis, Tooltip, Legend, Bar, LabelList, ResponsiveContainer, LineChart as LineChart$1, Line, PieChart, Pie, Cell, ReferenceLine, ComposedChart, Area, ScatterChart, Scatter } from 'recharts';
|
|
15
15
|
import { Slot } from '@radix-ui/react-slot';
|
|
16
|
-
import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, Minus, ArrowDown, ArrowUp, Settings2, CheckCircle2, Search, Loader2, AlertCircle, Edit2, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, RefreshCw, XCircle, ChevronLeft, ChevronRight,
|
|
16
|
+
import { Camera, ChevronDown, ChevronUp, Check, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, Minus, ArrowDown, ArrowUp, Settings2, CheckCircle2, Search, Loader2, AlertCircle, Edit2, CheckCircle, AlertTriangle, Info, Share2, Trophy, Target, Download, User, RefreshCw, Sliders, Sparkles, TrendingDown, TrendingUp, FolderOpen, Folder, HelpCircle, Play, Activity, Layers, Filter, XCircle, ChevronLeft, ChevronRight, Sun, Moon, MousePointer, ArrowRight, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, Package, Building2, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
|
|
17
17
|
import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
|
|
18
18
|
import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, UsersIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, ArrowLeftIcon, XCircleIcon, InformationCircleIcon } from '@heroicons/react/24/outline';
|
|
19
19
|
import { CheckIcon } from '@heroicons/react/24/solid';
|
|
@@ -268,14 +268,14 @@ var _getDashboardConfigInstance = () => {
|
|
|
268
268
|
}
|
|
269
269
|
return dashboardConfigInstance;
|
|
270
270
|
};
|
|
271
|
-
var DashboardConfigContext =
|
|
271
|
+
var DashboardConfigContext = React20.createContext(void 0);
|
|
272
272
|
var DashboardProvider = ({ config: userProvidedConfig, children }) => {
|
|
273
|
-
const fullConfig =
|
|
273
|
+
const fullConfig = React20.useMemo(() => mergeWithDefaultConfig(userProvidedConfig), [userProvidedConfig]);
|
|
274
274
|
_setDashboardConfigInstance(fullConfig);
|
|
275
|
-
|
|
275
|
+
React20.useEffect(() => {
|
|
276
276
|
_setDashboardConfigInstance(fullConfig);
|
|
277
277
|
}, [fullConfig]);
|
|
278
|
-
|
|
278
|
+
React20.useEffect(() => {
|
|
279
279
|
if (!fullConfig.theme) return;
|
|
280
280
|
const styleId = "dashboard-core-theme-vars";
|
|
281
281
|
let styleEl = document.getElementById(styleId);
|
|
@@ -301,7 +301,7 @@ var DashboardProvider = ({ config: userProvidedConfig, children }) => {
|
|
|
301
301
|
return /* @__PURE__ */ jsx(DashboardConfigContext.Provider, { value: fullConfig, children });
|
|
302
302
|
};
|
|
303
303
|
var useDashboardConfig = () => {
|
|
304
|
-
const ctx =
|
|
304
|
+
const ctx = React20.useContext(DashboardConfigContext);
|
|
305
305
|
if (!ctx) throw new Error("useDashboardConfig must be used within a DashboardProvider");
|
|
306
306
|
return ctx;
|
|
307
307
|
};
|
|
@@ -1281,7 +1281,18 @@ var dashboardService = {
|
|
|
1281
1281
|
const formattedStartDate = formatDate(startDate);
|
|
1282
1282
|
const formattedEndDate = formatDate(endDate);
|
|
1283
1283
|
try {
|
|
1284
|
-
const { data, error } = await supabase.from(metricsTable).select(
|
|
1284
|
+
const { data, error } = await supabase.from(metricsTable).select(`
|
|
1285
|
+
date,
|
|
1286
|
+
shift_id,
|
|
1287
|
+
efficiency,
|
|
1288
|
+
total_output,
|
|
1289
|
+
avg_cycle_time,
|
|
1290
|
+
avg_pph,
|
|
1291
|
+
pph_threshold,
|
|
1292
|
+
workspace_rank,
|
|
1293
|
+
idle_time,
|
|
1294
|
+
total_day_output
|
|
1295
|
+
`).eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
|
|
1285
1296
|
if (error) throw error;
|
|
1286
1297
|
if (!data) return [];
|
|
1287
1298
|
const transformedData = data.map((item) => ({
|
|
@@ -1291,7 +1302,8 @@ var dashboardService = {
|
|
|
1291
1302
|
avg_efficiency: item.efficiency || 0,
|
|
1292
1303
|
total_output: item.total_output || 0,
|
|
1293
1304
|
avg_cycle_time: item.avg_cycle_time || 0,
|
|
1294
|
-
ideal_output: item.
|
|
1305
|
+
ideal_output: item.total_day_output || 0,
|
|
1306
|
+
// Use daily target directly from performance_metrics table
|
|
1295
1307
|
avg_pph: item.avg_pph || 0,
|
|
1296
1308
|
pph_threshold: item.pph_threshold || 0,
|
|
1297
1309
|
workspace_rank: item.workspace_rank || 0,
|
|
@@ -3556,329 +3568,10 @@ var useAudioService = () => {
|
|
|
3556
3568
|
};
|
|
3557
3569
|
|
|
3558
3570
|
// src/lib/utils/dateShiftUtils.ts
|
|
3559
|
-
function isValidDateFormat(date) {
|
|
3560
|
-
return /^\d{4}-\d{2}-\d{2}$/.test(date);
|
|
3561
|
-
}
|
|
3562
3571
|
function isValidShiftId(shiftId) {
|
|
3563
3572
|
const id3 = typeof shiftId === "string" ? parseInt(shiftId, 10) : shiftId;
|
|
3564
3573
|
return id3 === 0 || id3 === 1;
|
|
3565
3574
|
}
|
|
3566
|
-
|
|
3567
|
-
// src/lib/api/s3-clips-parser.ts
|
|
3568
|
-
function parseS3Uri(s3Uri, sopCategories) {
|
|
3569
|
-
const path = new URL(s3Uri).pathname;
|
|
3570
|
-
const parts = path.split("/").filter((p) => p);
|
|
3571
|
-
if (s3Uri.includes("missed_qchecks")) {
|
|
3572
|
-
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
3573
|
-
return null;
|
|
3574
|
-
}
|
|
3575
|
-
if (parts.length < 8) {
|
|
3576
|
-
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
3577
|
-
return null;
|
|
3578
|
-
}
|
|
3579
|
-
try {
|
|
3580
|
-
const datePart = parts[2];
|
|
3581
|
-
const shiftPart = parts[3];
|
|
3582
|
-
const violationType = parts[4];
|
|
3583
|
-
let folderName = "";
|
|
3584
|
-
let timestamp = "";
|
|
3585
|
-
for (let i = 5; i < parts.length; i++) {
|
|
3586
|
-
const part = parts[i];
|
|
3587
|
-
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
3588
|
-
folderName = part;
|
|
3589
|
-
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
3590
|
-
if (timeMatch) {
|
|
3591
|
-
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
3592
|
-
break;
|
|
3593
|
-
}
|
|
3594
|
-
}
|
|
3595
|
-
}
|
|
3596
|
-
if (!timestamp) {
|
|
3597
|
-
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
3598
|
-
timestamp = "00:00:00";
|
|
3599
|
-
}
|
|
3600
|
-
let severity = "low";
|
|
3601
|
-
let type = "bottleneck";
|
|
3602
|
-
let description = "Analysis Clip";
|
|
3603
|
-
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
3604
|
-
if (sopCategories && sopCategories.length > 0) {
|
|
3605
|
-
const matchedCategory = sopCategories.find((category) => {
|
|
3606
|
-
const categoryId = category.id.toLowerCase();
|
|
3607
|
-
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
3608
|
-
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
3609
|
-
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
3610
|
-
});
|
|
3611
|
-
if (matchedCategory) {
|
|
3612
|
-
type = matchedCategory.id;
|
|
3613
|
-
description = matchedCategory.description || matchedCategory.label;
|
|
3614
|
-
if (matchedCategory.color.includes("red")) {
|
|
3615
|
-
severity = "high";
|
|
3616
|
-
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
3617
|
-
severity = "medium";
|
|
3618
|
-
} else {
|
|
3619
|
-
severity = "low";
|
|
3620
|
-
}
|
|
3621
|
-
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
3622
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
3623
|
-
}
|
|
3624
|
-
}
|
|
3625
|
-
switch (normalizedViolationType) {
|
|
3626
|
-
case "idle_time":
|
|
3627
|
-
case "idle":
|
|
3628
|
-
case "low_value":
|
|
3629
|
-
case "low value":
|
|
3630
|
-
case "low_value_moment":
|
|
3631
|
-
case "low_value_moments":
|
|
3632
|
-
case "low value moment":
|
|
3633
|
-
case "low value moments":
|
|
3634
|
-
type = "low_value";
|
|
3635
|
-
severity = "low";
|
|
3636
|
-
description = "Idle Time Detected";
|
|
3637
|
-
break;
|
|
3638
|
-
case "sop_deviation":
|
|
3639
|
-
type = "missing_quality_check";
|
|
3640
|
-
severity = "high";
|
|
3641
|
-
description = "SOP Deviations";
|
|
3642
|
-
break;
|
|
3643
|
-
case "long_cycle_time":
|
|
3644
|
-
severity = "high";
|
|
3645
|
-
type = "long_cycle_time";
|
|
3646
|
-
description = "Long Cycle Time Detected";
|
|
3647
|
-
break;
|
|
3648
|
-
case "best_cycle_time":
|
|
3649
|
-
type = "best_cycle_time";
|
|
3650
|
-
severity = "low";
|
|
3651
|
-
description = "Best Cycle Time Performance";
|
|
3652
|
-
break;
|
|
3653
|
-
case "worst_cycle_time":
|
|
3654
|
-
type = "worst_cycle_time";
|
|
3655
|
-
severity = "high";
|
|
3656
|
-
description = "Worst Cycle Time Performance";
|
|
3657
|
-
break;
|
|
3658
|
-
case "cycle_completion":
|
|
3659
|
-
case "completed_cycles":
|
|
3660
|
-
case "completed_cycle":
|
|
3661
|
-
type = "cycle_completion";
|
|
3662
|
-
severity = "low";
|
|
3663
|
-
description = "Cycle Completion";
|
|
3664
|
-
break;
|
|
3665
|
-
case "running_cycle":
|
|
3666
|
-
case "active_cycle":
|
|
3667
|
-
case "production_cycle":
|
|
3668
|
-
type = "running_cycle";
|
|
3669
|
-
severity = "low";
|
|
3670
|
-
description = "Active Production Cycle";
|
|
3671
|
-
break;
|
|
3672
|
-
case "setup_state":
|
|
3673
|
-
case "machine_setup":
|
|
3674
|
-
case "line_setup":
|
|
3675
|
-
type = "setup_state";
|
|
3676
|
-
severity = "medium";
|
|
3677
|
-
description = "Machine Setup Activity";
|
|
3678
|
-
break;
|
|
3679
|
-
case "medium_bottleneck":
|
|
3680
|
-
severity = "medium";
|
|
3681
|
-
description = "Medium Bottleneck Identified";
|
|
3682
|
-
break;
|
|
3683
|
-
case "minor_bottleneck":
|
|
3684
|
-
case "mild_bottleneck":
|
|
3685
|
-
severity = "low";
|
|
3686
|
-
description = "Minor Bottleneck Identified";
|
|
3687
|
-
break;
|
|
3688
|
-
default:
|
|
3689
|
-
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
3690
|
-
type = "missing_quality_check";
|
|
3691
|
-
severity = "high";
|
|
3692
|
-
description = "SOP Deviations";
|
|
3693
|
-
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
3694
|
-
type = "worst_cycle_time";
|
|
3695
|
-
severity = "high";
|
|
3696
|
-
description = "Worst Cycle Time Performance";
|
|
3697
|
-
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
3698
|
-
type = "best_cycle_time";
|
|
3699
|
-
severity = "low";
|
|
3700
|
-
description = "Best Cycle Time Performance";
|
|
3701
|
-
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
3702
|
-
type = "long_cycle_time";
|
|
3703
|
-
severity = "high";
|
|
3704
|
-
description = "Long Cycle Time Detected";
|
|
3705
|
-
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
3706
|
-
type = "cycle_completion";
|
|
3707
|
-
severity = "low";
|
|
3708
|
-
description = "Cycle Completion";
|
|
3709
|
-
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
3710
|
-
type = "running_cycle";
|
|
3711
|
-
severity = "low";
|
|
3712
|
-
description = "Active Production Cycle";
|
|
3713
|
-
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
3714
|
-
type = "setup_state";
|
|
3715
|
-
severity = "medium";
|
|
3716
|
-
description = "Machine Setup Activity";
|
|
3717
|
-
} else {
|
|
3718
|
-
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
3719
|
-
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
3720
|
-
}
|
|
3721
|
-
break;
|
|
3722
|
-
}
|
|
3723
|
-
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
3724
|
-
} catch (error) {
|
|
3725
|
-
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
3726
|
-
return null;
|
|
3727
|
-
}
|
|
3728
|
-
}
|
|
3729
|
-
function shuffleArray(array) {
|
|
3730
|
-
const shuffled = [...array];
|
|
3731
|
-
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
3732
|
-
const j = Math.floor(Math.random() * (i + 1));
|
|
3733
|
-
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
3734
|
-
}
|
|
3735
|
-
return shuffled;
|
|
3736
|
-
}
|
|
3737
|
-
var SmartVideoCache = class extends EventEmitter {
|
|
3738
|
-
constructor() {
|
|
3739
|
-
super();
|
|
3740
|
-
this.metrics = {
|
|
3741
|
-
hits: 0,
|
|
3742
|
-
misses: 0,
|
|
3743
|
-
evictions: 0,
|
|
3744
|
-
totalSize: 0,
|
|
3745
|
-
entryCount: 0,
|
|
3746
|
-
hitRate: 0
|
|
3747
|
-
};
|
|
3748
|
-
this.setMaxListeners(50);
|
|
3749
|
-
}
|
|
3750
|
-
/**
|
|
3751
|
-
* DISABLED - Always returns null
|
|
3752
|
-
*/
|
|
3753
|
-
async getSummary(key) {
|
|
3754
|
-
this.metrics.misses++;
|
|
3755
|
-
this.updateHitRate();
|
|
3756
|
-
return null;
|
|
3757
|
-
}
|
|
3758
|
-
/**
|
|
3759
|
-
* DISABLED - Does nothing
|
|
3760
|
-
*/
|
|
3761
|
-
async setSummary(key, summary) {
|
|
3762
|
-
}
|
|
3763
|
-
/**
|
|
3764
|
-
* DISABLED - Always returns null
|
|
3765
|
-
*/
|
|
3766
|
-
async getVideos(key) {
|
|
3767
|
-
this.metrics.misses++;
|
|
3768
|
-
this.updateHitRate();
|
|
3769
|
-
return null;
|
|
3770
|
-
}
|
|
3771
|
-
/**
|
|
3772
|
-
* DISABLED - Does nothing
|
|
3773
|
-
*/
|
|
3774
|
-
async setVideos(key, videos) {
|
|
3775
|
-
}
|
|
3776
|
-
/**
|
|
3777
|
-
* DISABLED - Always returns null
|
|
3778
|
-
*/
|
|
3779
|
-
async getClipCounts(key) {
|
|
3780
|
-
console.log("[SmartVideoCache] DISABLED - Returning null for clip counts");
|
|
3781
|
-
this.metrics.misses++;
|
|
3782
|
-
this.updateHitRate();
|
|
3783
|
-
return null;
|
|
3784
|
-
}
|
|
3785
|
-
/**
|
|
3786
|
-
* DISABLED - Does nothing
|
|
3787
|
-
*/
|
|
3788
|
-
async setClipCounts(key, clipCountsWithIndex, ttlMinutes) {
|
|
3789
|
-
console.log("[SmartVideoCache] DISABLED - Not caching clip counts");
|
|
3790
|
-
}
|
|
3791
|
-
/**
|
|
3792
|
-
* DISABLED - Does nothing
|
|
3793
|
-
*/
|
|
3794
|
-
setPrefetchStatus(key, status) {
|
|
3795
|
-
}
|
|
3796
|
-
/**
|
|
3797
|
-
* DISABLED - Always returns NONE
|
|
3798
|
-
*/
|
|
3799
|
-
getPrefetchStatus(key) {
|
|
3800
|
-
return "none" /* NONE */;
|
|
3801
|
-
}
|
|
3802
|
-
/**
|
|
3803
|
-
* DISABLED - Always returns false
|
|
3804
|
-
*/
|
|
3805
|
-
isPrefetchReady(key) {
|
|
3806
|
-
return false;
|
|
3807
|
-
}
|
|
3808
|
-
/**
|
|
3809
|
-
* DISABLED - Returns empty cleanup function
|
|
3810
|
-
*/
|
|
3811
|
-
subscribeToPrefetchStatus(key, callback) {
|
|
3812
|
-
return () => {
|
|
3813
|
-
};
|
|
3814
|
-
}
|
|
3815
|
-
/**
|
|
3816
|
-
* DISABLED - Always returns null
|
|
3817
|
-
*/
|
|
3818
|
-
getSignedUrl(s3Uri) {
|
|
3819
|
-
this.metrics.misses++;
|
|
3820
|
-
this.updateHitRate();
|
|
3821
|
-
return null;
|
|
3822
|
-
}
|
|
3823
|
-
/**
|
|
3824
|
-
* DISABLED - Does nothing
|
|
3825
|
-
*/
|
|
3826
|
-
setSignedUrl(s3Uri, url, ttlSeconds = 3600) {
|
|
3827
|
-
}
|
|
3828
|
-
/**
|
|
3829
|
-
* DISABLED - Always returns empty array
|
|
3830
|
-
*/
|
|
3831
|
-
async getVideosByWorkspace(workspaceId) {
|
|
3832
|
-
return [];
|
|
3833
|
-
}
|
|
3834
|
-
/**
|
|
3835
|
-
* DISABLED - Does nothing
|
|
3836
|
-
*/
|
|
3837
|
-
async warmup(entries) {
|
|
3838
|
-
}
|
|
3839
|
-
/**
|
|
3840
|
-
* Get cache statistics (mostly zeros since cache is disabled)
|
|
3841
|
-
*/
|
|
3842
|
-
getStats() {
|
|
3843
|
-
return {
|
|
3844
|
-
...this.metrics,
|
|
3845
|
-
summaryStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3846
|
-
videoStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3847
|
-
clipCountsStats: { hits: 0, misses: this.metrics.misses, evictions: 0, totalSize: 0 },
|
|
3848
|
-
urlCacheSize: 0
|
|
3849
|
-
};
|
|
3850
|
-
}
|
|
3851
|
-
/**
|
|
3852
|
-
* DISABLED - Does nothing
|
|
3853
|
-
*/
|
|
3854
|
-
clear(type) {
|
|
3855
|
-
this.removeAllListeners();
|
|
3856
|
-
}
|
|
3857
|
-
/**
|
|
3858
|
-
* Update hit rate metric
|
|
3859
|
-
*/
|
|
3860
|
-
updateHitRate() {
|
|
3861
|
-
const total = this.metrics.hits + this.metrics.misses;
|
|
3862
|
-
this.metrics.hitRate = total > 0 ? this.metrics.hits / total : 0;
|
|
3863
|
-
}
|
|
3864
|
-
/**
|
|
3865
|
-
* Export cache state for debugging (returns empty state)
|
|
3866
|
-
*/
|
|
3867
|
-
exportState() {
|
|
3868
|
-
return {
|
|
3869
|
-
summaries: [],
|
|
3870
|
-
videos: [],
|
|
3871
|
-
urls: [],
|
|
3872
|
-
metrics: this.getStats()
|
|
3873
|
-
};
|
|
3874
|
-
}
|
|
3875
|
-
};
|
|
3876
|
-
var smartVideoCache = new SmartVideoCache();
|
|
3877
|
-
if (typeof window !== "undefined") {
|
|
3878
|
-
window.addEventListener("beforeunload", () => {
|
|
3879
|
-
console.log("[SmartVideoCache] Cache disabled - no cleanup needed");
|
|
3880
|
-
});
|
|
3881
|
-
}
|
|
3882
3575
|
var getSupabaseClient = () => {
|
|
3883
3576
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
3884
3577
|
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
@@ -3891,616 +3584,6 @@ var getAuthToken = async () => {
|
|
|
3891
3584
|
try {
|
|
3892
3585
|
const supabase = getSupabaseClient();
|
|
3893
3586
|
const { data: { session } } = await supabase.auth.getSession();
|
|
3894
|
-
return session?.access_token || null;
|
|
3895
|
-
} catch (error) {
|
|
3896
|
-
console.error("[S3ClipsAPIClient] Error getting auth token:", error);
|
|
3897
|
-
return null;
|
|
3898
|
-
}
|
|
3899
|
-
};
|
|
3900
|
-
var S3ClipsAPIClient = class {
|
|
3901
|
-
constructor(sopCategories) {
|
|
3902
|
-
this.baseUrl = "/api/clips";
|
|
3903
|
-
this.requestCache = /* @__PURE__ */ new Map();
|
|
3904
|
-
this.sopCategories = sopCategories;
|
|
3905
|
-
console.log("[S3ClipsAPIClient] \u2705 Initialized - Using secure API routes (no direct S3 access)");
|
|
3906
|
-
}
|
|
3907
|
-
/**
|
|
3908
|
-
* Fetch with authentication and error handling
|
|
3909
|
-
*/
|
|
3910
|
-
async fetchWithAuth(endpoint, body) {
|
|
3911
|
-
const token = await getAuthToken();
|
|
3912
|
-
if (!token) {
|
|
3913
|
-
throw new Error("Authentication required");
|
|
3914
|
-
}
|
|
3915
|
-
const response = await fetch(endpoint, {
|
|
3916
|
-
method: "POST",
|
|
3917
|
-
headers: {
|
|
3918
|
-
"Authorization": `Bearer ${token}`,
|
|
3919
|
-
"Content-Type": "application/json"
|
|
3920
|
-
},
|
|
3921
|
-
body: JSON.stringify(body)
|
|
3922
|
-
});
|
|
3923
|
-
if (!response.ok) {
|
|
3924
|
-
const error = await response.json().catch(() => ({ error: "Request failed" }));
|
|
3925
|
-
throw new Error(error.error || `API error: ${response.status}`);
|
|
3926
|
-
}
|
|
3927
|
-
return response.json();
|
|
3928
|
-
}
|
|
3929
|
-
/**
|
|
3930
|
-
* Deduplicate requests to prevent multiple API calls
|
|
3931
|
-
*/
|
|
3932
|
-
async deduplicate(key, factory) {
|
|
3933
|
-
if (this.requestCache.has(key)) {
|
|
3934
|
-
console.log(`[S3ClipsAPIClient] Deduplicating request: ${key}`);
|
|
3935
|
-
return this.requestCache.get(key);
|
|
3936
|
-
}
|
|
3937
|
-
const promise = factory().finally(() => {
|
|
3938
|
-
this.requestCache.delete(key);
|
|
3939
|
-
});
|
|
3940
|
-
this.requestCache.set(key, promise);
|
|
3941
|
-
return promise;
|
|
3942
|
-
}
|
|
3943
|
-
/**
|
|
3944
|
-
* List S3 clips
|
|
3945
|
-
*/
|
|
3946
|
-
async listS3Clips(params) {
|
|
3947
|
-
const cacheKey = `list:${JSON.stringify(params)}`;
|
|
3948
|
-
return this.deduplicate(cacheKey, async () => {
|
|
3949
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
3950
|
-
action: "list",
|
|
3951
|
-
workspaceId: params.workspaceId,
|
|
3952
|
-
date: params.date,
|
|
3953
|
-
shift: params.shiftId,
|
|
3954
|
-
maxKeys: params.maxKeys
|
|
3955
|
-
});
|
|
3956
|
-
return response.clips.map((clip) => clip.originalUri);
|
|
3957
|
-
});
|
|
3958
|
-
}
|
|
3959
|
-
/**
|
|
3960
|
-
* Get clip counts
|
|
3961
|
-
*/
|
|
3962
|
-
async getClipCounts(workspaceId, date, shiftId) {
|
|
3963
|
-
const cacheKey = `counts:${workspaceId}:${date}:${shiftId}`;
|
|
3964
|
-
return this.deduplicate(cacheKey, async () => {
|
|
3965
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
3966
|
-
action: "count",
|
|
3967
|
-
workspaceId,
|
|
3968
|
-
date,
|
|
3969
|
-
shift: shiftId.toString()
|
|
3970
|
-
});
|
|
3971
|
-
return response.counts;
|
|
3972
|
-
});
|
|
3973
|
-
}
|
|
3974
|
-
/**
|
|
3975
|
-
* Get clip counts with index (for compatibility)
|
|
3976
|
-
*/
|
|
3977
|
-
async getClipCountsWithIndex(workspaceId, date, shiftId) {
|
|
3978
|
-
const counts = await this.getClipCounts(workspaceId, date, shiftId.toString());
|
|
3979
|
-
const videoIndex = {
|
|
3980
|
-
byCategory: /* @__PURE__ */ new Map(),
|
|
3981
|
-
allVideos: [],
|
|
3982
|
-
counts,
|
|
3983
|
-
workspaceId,
|
|
3984
|
-
date,
|
|
3985
|
-
shiftId: shiftId.toString(),
|
|
3986
|
-
lastUpdated: /* @__PURE__ */ new Date()
|
|
3987
|
-
};
|
|
3988
|
-
return { counts, videoIndex };
|
|
3989
|
-
}
|
|
3990
|
-
/**
|
|
3991
|
-
* Get metadata for a video
|
|
3992
|
-
*/
|
|
3993
|
-
async getMetadata(playlistUri) {
|
|
3994
|
-
const cacheKey = `metadata:${playlistUri}`;
|
|
3995
|
-
return this.deduplicate(cacheKey, async () => {
|
|
3996
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
3997
|
-
action: "metadata",
|
|
3998
|
-
playlistUri
|
|
3999
|
-
});
|
|
4000
|
-
return response.metadata;
|
|
4001
|
-
});
|
|
4002
|
-
}
|
|
4003
|
-
/**
|
|
4004
|
-
* Get metadata cycle time
|
|
4005
|
-
*/
|
|
4006
|
-
async getMetadataCycleTime(playlistUri) {
|
|
4007
|
-
const metadata = await this.getMetadata(playlistUri);
|
|
4008
|
-
return metadata?.cycle_time_seconds || null;
|
|
4009
|
-
}
|
|
4010
|
-
/**
|
|
4011
|
-
* Get first clip for category
|
|
4012
|
-
*/
|
|
4013
|
-
async getFirstClipForCategory(workspaceId, date, shiftId, category) {
|
|
4014
|
-
const cacheKey = `first:${workspaceId}:${date}:${shiftId}:${category}`;
|
|
4015
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4016
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4017
|
-
action: "first",
|
|
4018
|
-
workspaceId,
|
|
4019
|
-
date,
|
|
4020
|
-
shift: shiftId.toString(),
|
|
4021
|
-
category,
|
|
4022
|
-
sopCategories: this.sopCategories
|
|
4023
|
-
});
|
|
4024
|
-
return response.video;
|
|
4025
|
-
});
|
|
4026
|
-
}
|
|
4027
|
-
/**
|
|
4028
|
-
* Get clip by index
|
|
4029
|
-
*/
|
|
4030
|
-
async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
|
|
4031
|
-
const cacheKey = `by-index:${workspaceId}:${date}:${shiftId}:${category}:${index}`;
|
|
4032
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4033
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4034
|
-
action: "by-index",
|
|
4035
|
-
workspaceId,
|
|
4036
|
-
date,
|
|
4037
|
-
shift: shiftId.toString(),
|
|
4038
|
-
category,
|
|
4039
|
-
index,
|
|
4040
|
-
sopCategories: this.sopCategories
|
|
4041
|
-
});
|
|
4042
|
-
const video = response.video;
|
|
4043
|
-
if (video && includeMetadata && video.originalUri) {
|
|
4044
|
-
try {
|
|
4045
|
-
const metadata = await this.getMetadata(video.originalUri);
|
|
4046
|
-
if (metadata) {
|
|
4047
|
-
video.cycle_time_seconds = metadata.cycle_time_seconds;
|
|
4048
|
-
video.creation_timestamp = metadata.creation_timestamp;
|
|
4049
|
-
}
|
|
4050
|
-
} catch (error) {
|
|
4051
|
-
console.warn("[S3ClipsAPIClient] Failed to fetch metadata:", error);
|
|
4052
|
-
}
|
|
4053
|
-
}
|
|
4054
|
-
return video;
|
|
4055
|
-
});
|
|
4056
|
-
}
|
|
4057
|
-
/**
|
|
4058
|
-
* Get videos page with pagination
|
|
4059
|
-
*/
|
|
4060
|
-
async getVideosPage(workspaceId, date, shiftId, category, pageSize = 5, startAfter) {
|
|
4061
|
-
const cacheKey = `page:${workspaceId}:${date}:${shiftId}:${category}:${pageSize}:${startAfter || "first"}`;
|
|
4062
|
-
return this.deduplicate(cacheKey, async () => {
|
|
4063
|
-
const response = await this.fetchWithAuth(this.baseUrl, {
|
|
4064
|
-
action: "page",
|
|
4065
|
-
workspaceId,
|
|
4066
|
-
date,
|
|
4067
|
-
shift: shiftId.toString(),
|
|
4068
|
-
category,
|
|
4069
|
-
pageSize,
|
|
4070
|
-
startAfter,
|
|
4071
|
-
sopCategories: this.sopCategories
|
|
4072
|
-
});
|
|
4073
|
-
return {
|
|
4074
|
-
videos: response.videos,
|
|
4075
|
-
nextToken: response.nextToken,
|
|
4076
|
-
hasMore: response.hasMore
|
|
4077
|
-
};
|
|
4078
|
-
});
|
|
4079
|
-
}
|
|
4080
|
-
/**
|
|
4081
|
-
* Batch fetch multiple videos in parallel
|
|
4082
|
-
*/
|
|
4083
|
-
async batchFetchVideos(workspaceId, date, shiftId, requests) {
|
|
4084
|
-
const batchKey = `batch:${workspaceId}:${date}:${shiftId}:${requests.length}`;
|
|
4085
|
-
return this.deduplicate(batchKey, async () => {
|
|
4086
|
-
const response = await this.fetchWithAuth("/api/clips/batch", {
|
|
4087
|
-
workspaceId,
|
|
4088
|
-
date,
|
|
4089
|
-
shift: shiftId.toString(),
|
|
4090
|
-
requests,
|
|
4091
|
-
sopCategories: this.sopCategories
|
|
4092
|
-
});
|
|
4093
|
-
console.log(`[S3ClipsAPIClient] Batch fetched ${response.videos.length} videos in ${response.performance.duration}ms`);
|
|
4094
|
-
return response.videos;
|
|
4095
|
-
});
|
|
4096
|
-
}
|
|
4097
|
-
/**
|
|
4098
|
-
* Convert S3 URI to CloudFront URL
|
|
4099
|
-
* In the API client, URLs are already signed from the server
|
|
4100
|
-
*/
|
|
4101
|
-
s3UriToCloudfront(s3Uri) {
|
|
4102
|
-
return s3Uri;
|
|
4103
|
-
}
|
|
4104
|
-
/**
|
|
4105
|
-
* Clean up resources
|
|
4106
|
-
*/
|
|
4107
|
-
dispose() {
|
|
4108
|
-
this.requestCache.clear();
|
|
4109
|
-
}
|
|
4110
|
-
/**
|
|
4111
|
-
* Get service statistics
|
|
4112
|
-
*/
|
|
4113
|
-
getStats() {
|
|
4114
|
-
return {
|
|
4115
|
-
requestCache: {
|
|
4116
|
-
pendingCount: this.requestCache.size,
|
|
4117
|
-
maxSize: 1e3
|
|
4118
|
-
}
|
|
4119
|
-
};
|
|
4120
|
-
}
|
|
4121
|
-
};
|
|
4122
|
-
|
|
4123
|
-
// src/lib/api/s3-clips-secure.ts
|
|
4124
|
-
var S3ClipsService = class {
|
|
4125
|
-
constructor(config) {
|
|
4126
|
-
// Flags for compatibility
|
|
4127
|
-
this.isIndexBuilding = false;
|
|
4128
|
-
this.isPrefetching = false;
|
|
4129
|
-
this.currentMetadataFetches = 0;
|
|
4130
|
-
this.MAX_CONCURRENT_METADATA = 3;
|
|
4131
|
-
this.config = config;
|
|
4132
|
-
if (!config.s3Config) {
|
|
4133
|
-
throw new Error("S3 configuration is required");
|
|
4134
|
-
}
|
|
4135
|
-
const sopCategories = config.s3Config.sopCategories?.default;
|
|
4136
|
-
this.apiClient = new S3ClipsAPIClient(sopCategories);
|
|
4137
|
-
const processing = config.s3Config.processing || {};
|
|
4138
|
-
this.defaultLimitPerCategory = processing.defaultLimitPerCategory || 30;
|
|
4139
|
-
this.maxLimitPerCategory = processing.maxLimitPerCategory || 1e3;
|
|
4140
|
-
this.concurrencyLimit = processing.concurrencyLimit || 10;
|
|
4141
|
-
this.maxInitialFetch = processing.maxInitialFetch || 60;
|
|
4142
|
-
console.log("[S3ClipsService] \u2705 Initialized with secure API client - AWS credentials are now server-side only!");
|
|
4143
|
-
}
|
|
4144
|
-
/**
|
|
4145
|
-
* Lists S3 clips using API
|
|
4146
|
-
*/
|
|
4147
|
-
async listS3Clips(params) {
|
|
4148
|
-
const { workspaceId, date, shiftId } = params;
|
|
4149
|
-
if (!isValidShiftId(shiftId)) {
|
|
4150
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4151
|
-
return [];
|
|
4152
|
-
}
|
|
4153
|
-
console.log(`[S3ClipsService] Listing clips via API for workspace: ${workspaceId}`);
|
|
4154
|
-
try {
|
|
4155
|
-
return await this.apiClient.listS3Clips(params);
|
|
4156
|
-
} catch (error) {
|
|
4157
|
-
console.error("[S3ClipsService] Error listing clips:", error);
|
|
4158
|
-
return [];
|
|
4159
|
-
}
|
|
4160
|
-
}
|
|
4161
|
-
/**
|
|
4162
|
-
* Get metadata cycle time
|
|
4163
|
-
*/
|
|
4164
|
-
async getMetadataCycleTime(playlistUri) {
|
|
4165
|
-
try {
|
|
4166
|
-
return await this.apiClient.getMetadataCycleTime(playlistUri);
|
|
4167
|
-
} catch (error) {
|
|
4168
|
-
console.error("[S3ClipsService] Error fetching metadata cycle time:", error);
|
|
4169
|
-
return null;
|
|
4170
|
-
}
|
|
4171
|
-
}
|
|
4172
|
-
/**
|
|
4173
|
-
* Control prefetch mode
|
|
4174
|
-
*/
|
|
4175
|
-
setPrefetchMode(enabled) {
|
|
4176
|
-
this.isPrefetching = enabled;
|
|
4177
|
-
console.log(`[S3ClipsService] Prefetch mode ${enabled ? "enabled" : "disabled"}`);
|
|
4178
|
-
}
|
|
4179
|
-
/**
|
|
4180
|
-
* Get full metadata
|
|
4181
|
-
*/
|
|
4182
|
-
async getFullMetadata(playlistUri) {
|
|
4183
|
-
if (this.isIndexBuilding || this.isPrefetching) {
|
|
4184
|
-
console.warn("[S3ClipsService] Skipping metadata - operation in progress");
|
|
4185
|
-
return null;
|
|
4186
|
-
}
|
|
4187
|
-
if (this.currentMetadataFetches >= this.MAX_CONCURRENT_METADATA) {
|
|
4188
|
-
console.warn("[S3ClipsService] Skipping metadata - max concurrent fetches reached");
|
|
4189
|
-
return null;
|
|
4190
|
-
}
|
|
4191
|
-
this.currentMetadataFetches++;
|
|
4192
|
-
try {
|
|
4193
|
-
return await this.apiClient.getMetadata(playlistUri);
|
|
4194
|
-
} catch (error) {
|
|
4195
|
-
console.error("[S3ClipsService] Error fetching metadata:", error);
|
|
4196
|
-
return null;
|
|
4197
|
-
} finally {
|
|
4198
|
-
this.currentMetadataFetches--;
|
|
4199
|
-
}
|
|
4200
|
-
}
|
|
4201
|
-
/**
|
|
4202
|
-
* Convert S3 URI to CloudFront URL
|
|
4203
|
-
* URLs from API are already signed
|
|
4204
|
-
*/
|
|
4205
|
-
s3UriToCloudfront(s3Uri) {
|
|
4206
|
-
if (s3Uri.startsWith("http")) {
|
|
4207
|
-
return s3Uri;
|
|
4208
|
-
}
|
|
4209
|
-
console.warn("[S3ClipsService] Unexpected S3 URI in secure mode:", s3Uri);
|
|
4210
|
-
return s3Uri;
|
|
4211
|
-
}
|
|
4212
|
-
/**
|
|
4213
|
-
* Get SOP categories for workspace
|
|
4214
|
-
*/
|
|
4215
|
-
getSOPCategories(workspaceId) {
|
|
4216
|
-
const sopConfig = this.config.s3Config?.sopCategories;
|
|
4217
|
-
if (!sopConfig) return void 0;
|
|
4218
|
-
if (sopConfig.workspaceOverrides && sopConfig.workspaceOverrides[workspaceId]) {
|
|
4219
|
-
return sopConfig.workspaceOverrides[workspaceId];
|
|
4220
|
-
}
|
|
4221
|
-
return sopConfig.default;
|
|
4222
|
-
}
|
|
4223
|
-
async getClipCounts(workspaceId, date, shiftId, buildIndex) {
|
|
4224
|
-
if (!isValidShiftId(shiftId)) {
|
|
4225
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4226
|
-
return buildIndex ? { counts: {}, videoIndex: this.createEmptyVideoIndex(workspaceId, date, shiftId.toString()) } : {};
|
|
4227
|
-
}
|
|
4228
|
-
try {
|
|
4229
|
-
if (buildIndex) {
|
|
4230
|
-
const result = await this.apiClient.getClipCountsWithIndex(workspaceId, date, shiftId);
|
|
4231
|
-
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
4232
|
-
await smartVideoCache.setClipCounts(cacheKey, result);
|
|
4233
|
-
return result;
|
|
4234
|
-
} else {
|
|
4235
|
-
const counts = await this.apiClient.getClipCounts(workspaceId, date, shiftId);
|
|
4236
|
-
return counts;
|
|
4237
|
-
}
|
|
4238
|
-
} catch (error) {
|
|
4239
|
-
console.error("[S3ClipsService] Error fetching clip counts:", error);
|
|
4240
|
-
return buildIndex ? { counts: {}, videoIndex: this.createEmptyVideoIndex(workspaceId, date, shiftId.toString()) } : {};
|
|
4241
|
-
}
|
|
4242
|
-
}
|
|
4243
|
-
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex) {
|
|
4244
|
-
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
4245
|
-
const cachedResult = await smartVideoCache.getClipCounts(cacheKey);
|
|
4246
|
-
if (cachedResult) {
|
|
4247
|
-
console.log("[S3ClipsService] Using cached clip counts");
|
|
4248
|
-
return buildIndex ? cachedResult : cachedResult.counts;
|
|
4249
|
-
}
|
|
4250
|
-
console.log("[S3ClipsService] Cache miss - fetching from API");
|
|
4251
|
-
return buildIndex ? this.getClipCounts(workspaceId, date, shiftId, true) : this.getClipCounts(workspaceId, date, shiftId);
|
|
4252
|
-
}
|
|
4253
|
-
/**
|
|
4254
|
-
* Get first clip for category
|
|
4255
|
-
*/
|
|
4256
|
-
async getFirstClipForCategory(workspaceId, date, shiftId, category) {
|
|
4257
|
-
if (!isValidShiftId(shiftId)) {
|
|
4258
|
-
console.error(`[S3ClipsService] Invalid shift ID: ${shiftId}`);
|
|
4259
|
-
return null;
|
|
4260
|
-
}
|
|
4261
|
-
try {
|
|
4262
|
-
return await this.apiClient.getFirstClipForCategory(workspaceId, date, shiftId, category);
|
|
4263
|
-
} catch (error) {
|
|
4264
|
-
console.error("[S3ClipsService] Error fetching first clip:", error);
|
|
4265
|
-
return null;
|
|
4266
|
-
}
|
|
4267
|
-
}
|
|
4268
|
-
/**
|
|
4269
|
-
* Get video from index (for compatibility)
|
|
4270
|
-
*/
|
|
4271
|
-
async getVideoFromIndex(videoIndex, category, index, includeCycleTime = true, includeMetadata = true) {
|
|
4272
|
-
const { workspaceId, date, shiftId } = videoIndex;
|
|
4273
|
-
return this.getClipByIndex(
|
|
4274
|
-
workspaceId,
|
|
4275
|
-
date,
|
|
4276
|
-
shiftId,
|
|
4277
|
-
category,
|
|
4278
|
-
index,
|
|
4279
|
-
includeCycleTime,
|
|
4280
|
-
includeMetadata
|
|
4281
|
-
);
|
|
4282
|
-
}
|
|
4283
|
-
/**
|
|
4284
|
-
* Get clip by index
|
|
4285
|
-
*/
|
|
4286
|
-
async getClipByIndex(workspaceId, date, shiftId, category, index, includeCycleTime = true, includeMetadata = false) {
|
|
4287
|
-
try {
|
|
4288
|
-
return await this.apiClient.getClipByIndex(
|
|
4289
|
-
workspaceId,
|
|
4290
|
-
date,
|
|
4291
|
-
shiftId,
|
|
4292
|
-
category,
|
|
4293
|
-
index,
|
|
4294
|
-
includeCycleTime,
|
|
4295
|
-
includeMetadata
|
|
4296
|
-
);
|
|
4297
|
-
} catch (error) {
|
|
4298
|
-
console.error("[S3ClipsService] Error fetching clip by index:", error);
|
|
4299
|
-
return null;
|
|
4300
|
-
}
|
|
4301
|
-
}
|
|
4302
|
-
/**
|
|
4303
|
-
* Process full video (for compatibility)
|
|
4304
|
-
*/
|
|
4305
|
-
async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
|
|
4306
|
-
const sopCategories = this.getSOPCategories(workspaceId);
|
|
4307
|
-
const parsedInfo = parseS3Uri(uri, sopCategories);
|
|
4308
|
-
if (!parsedInfo) {
|
|
4309
|
-
console.warn(`Skipping URI due to parsing failure: ${uri}`);
|
|
4310
|
-
return null;
|
|
4311
|
-
}
|
|
4312
|
-
const video = {
|
|
4313
|
-
id: `${workspaceId}-${date}-${shiftId}-${index}`,
|
|
4314
|
-
src: uri,
|
|
4315
|
-
// Already signed from API
|
|
4316
|
-
...parsedInfo,
|
|
4317
|
-
originalUri: uri
|
|
4318
|
-
};
|
|
4319
|
-
if (includeMetadata) {
|
|
4320
|
-
const metadata = await this.getFullMetadata(uri);
|
|
4321
|
-
if (metadata) {
|
|
4322
|
-
video.cycle_time_seconds = metadata.original_task_metadata?.cycle_time;
|
|
4323
|
-
video.creation_timestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp;
|
|
4324
|
-
}
|
|
4325
|
-
}
|
|
4326
|
-
return video;
|
|
4327
|
-
}
|
|
4328
|
-
/**
|
|
4329
|
-
* Fetch clips (main method for compatibility)
|
|
4330
|
-
*/
|
|
4331
|
-
async fetchClips(params) {
|
|
4332
|
-
const {
|
|
4333
|
-
workspaceId,
|
|
4334
|
-
date: inputDate,
|
|
4335
|
-
shift,
|
|
4336
|
-
category,
|
|
4337
|
-
limit,
|
|
4338
|
-
offset = 0,
|
|
4339
|
-
mode
|
|
4340
|
-
} = params;
|
|
4341
|
-
if (!workspaceId) {
|
|
4342
|
-
throw new Error("Valid Workspace ID is required");
|
|
4343
|
-
}
|
|
4344
|
-
const date = inputDate || getOperationalDate(
|
|
4345
|
-
this.config.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
|
|
4346
|
-
/* @__PURE__ */ new Date(),
|
|
4347
|
-
this.config.shiftConfig?.dayShift?.startTime || "06:00"
|
|
4348
|
-
);
|
|
4349
|
-
if (!isValidDateFormat(date)) {
|
|
4350
|
-
throw new Error("Invalid date format. Use YYYY-MM-DD.");
|
|
4351
|
-
}
|
|
4352
|
-
let shiftId;
|
|
4353
|
-
if (shift !== void 0) {
|
|
4354
|
-
if (!isValidShiftId(shift)) {
|
|
4355
|
-
throw new Error("Invalid shift value. Must be 0 (day) or 1 (night).");
|
|
4356
|
-
}
|
|
4357
|
-
shiftId = parseInt(shift, 10);
|
|
4358
|
-
} else {
|
|
4359
|
-
const { shiftId: currentShiftId } = getCurrentShift(
|
|
4360
|
-
this.config.dateTimeConfig?.defaultTimezone || "Asia/Kolkata",
|
|
4361
|
-
this.config.shiftConfig
|
|
4362
|
-
);
|
|
4363
|
-
shiftId = currentShiftId;
|
|
4364
|
-
}
|
|
4365
|
-
console.log(`[S3ClipsService] Fetching clips for workspace ${workspaceId}`);
|
|
4366
|
-
if (mode === "summary") {
|
|
4367
|
-
const counts = await this.getClipCounts(workspaceId, date, shiftId.toString());
|
|
4368
|
-
return { counts, samples: {} };
|
|
4369
|
-
}
|
|
4370
|
-
const effectiveLimit = limit || this.defaultLimitPerCategory;
|
|
4371
|
-
const result = await this.getVideosPage(
|
|
4372
|
-
workspaceId,
|
|
4373
|
-
date,
|
|
4374
|
-
shiftId,
|
|
4375
|
-
category || "all",
|
|
4376
|
-
effectiveLimit
|
|
4377
|
-
);
|
|
4378
|
-
return result.videos;
|
|
4379
|
-
}
|
|
4380
|
-
/**
|
|
4381
|
-
* Batch fetch multiple videos in parallel
|
|
4382
|
-
*/
|
|
4383
|
-
async batchFetchVideos(workspaceId, date, shiftId, requests) {
|
|
4384
|
-
try {
|
|
4385
|
-
const results = await this.apiClient.batchFetchVideos(
|
|
4386
|
-
workspaceId,
|
|
4387
|
-
date,
|
|
4388
|
-
shiftId,
|
|
4389
|
-
requests
|
|
4390
|
-
);
|
|
4391
|
-
return results.map((r2) => r2.video).filter((v) => v !== null);
|
|
4392
|
-
} catch (error) {
|
|
4393
|
-
console.error("[S3ClipsService] Error batch fetching videos:", error);
|
|
4394
|
-
return [];
|
|
4395
|
-
}
|
|
4396
|
-
}
|
|
4397
|
-
/**
|
|
4398
|
-
* Get videos page using pagination API
|
|
4399
|
-
*/
|
|
4400
|
-
async getVideosPage(workspaceId, date, shiftId, category, pageSize = 5, startAfter) {
|
|
4401
|
-
try {
|
|
4402
|
-
return await this.apiClient.getVideosPage(
|
|
4403
|
-
workspaceId,
|
|
4404
|
-
date,
|
|
4405
|
-
shiftId,
|
|
4406
|
-
category,
|
|
4407
|
-
pageSize,
|
|
4408
|
-
startAfter
|
|
4409
|
-
);
|
|
4410
|
-
} catch (error) {
|
|
4411
|
-
console.error("[S3ClipsService] Error fetching videos page:", error);
|
|
4412
|
-
return { videos: [], hasMore: false };
|
|
4413
|
-
}
|
|
4414
|
-
}
|
|
4415
|
-
/**
|
|
4416
|
-
* Create empty video index for compatibility
|
|
4417
|
-
*/
|
|
4418
|
-
createEmptyVideoIndex(workspaceId, date, shiftId) {
|
|
4419
|
-
return {
|
|
4420
|
-
byCategory: /* @__PURE__ */ new Map(),
|
|
4421
|
-
allVideos: [],
|
|
4422
|
-
counts: {},
|
|
4423
|
-
workspaceId,
|
|
4424
|
-
date,
|
|
4425
|
-
shiftId,
|
|
4426
|
-
lastUpdated: /* @__PURE__ */ new Date(),
|
|
4427
|
-
_debugId: `empty_${Date.now()}`
|
|
4428
|
-
};
|
|
4429
|
-
}
|
|
4430
|
-
/**
|
|
4431
|
-
* Get clip by ID
|
|
4432
|
-
*/
|
|
4433
|
-
async getClipById(clipId, sopCategories) {
|
|
4434
|
-
console.log(`[S3ClipsService] Getting clip by ID: ${clipId} (Note: This is a fallback implementation)`);
|
|
4435
|
-
try {
|
|
4436
|
-
const parts = clipId.split("-");
|
|
4437
|
-
if (parts.length >= 5) {
|
|
4438
|
-
const workspaceId = parts[0];
|
|
4439
|
-
const date = parts[1];
|
|
4440
|
-
const shift = parts[2];
|
|
4441
|
-
const category = parts[3];
|
|
4442
|
-
const index = parseInt(parts[4], 10);
|
|
4443
|
-
if (!isNaN(index)) {
|
|
4444
|
-
return await this.getClipByIndex(workspaceId, date, shift, category, index, true, false);
|
|
4445
|
-
}
|
|
4446
|
-
}
|
|
4447
|
-
console.warn(`[S3ClipsService] Could not parse clipId: ${clipId}`);
|
|
4448
|
-
return null;
|
|
4449
|
-
} catch (error) {
|
|
4450
|
-
console.error("[S3ClipsService] Error getting clip by ID:", error);
|
|
4451
|
-
return null;
|
|
4452
|
-
}
|
|
4453
|
-
}
|
|
4454
|
-
/**
|
|
4455
|
-
* Get neighboring clips
|
|
4456
|
-
*/
|
|
4457
|
-
async getNeighboringClips(workspaceId, date, shiftId, category, currentClipId, sopCategories) {
|
|
4458
|
-
console.log(`[S3ClipsService] Getting neighboring clips for ID: ${currentClipId} (Note: This is a fallback implementation)`);
|
|
4459
|
-
try {
|
|
4460
|
-
const parts = currentClipId.split("-");
|
|
4461
|
-
if (parts.length >= 5) {
|
|
4462
|
-
const index = parseInt(parts[4], 10);
|
|
4463
|
-
if (!isNaN(index)) {
|
|
4464
|
-
const [previous, next] = await Promise.all([
|
|
4465
|
-
index > 0 ? this.getClipByIndex(workspaceId, date, shiftId, category, index - 1, true, false) : null,
|
|
4466
|
-
this.getClipByIndex(workspaceId, date, shiftId, category, index + 1, true, false)
|
|
4467
|
-
]);
|
|
4468
|
-
return { previous, next };
|
|
4469
|
-
}
|
|
4470
|
-
}
|
|
4471
|
-
console.warn(`[S3ClipsService] Could not parse currentClipId: ${currentClipId}`);
|
|
4472
|
-
return { previous: null, next: null };
|
|
4473
|
-
} catch (error) {
|
|
4474
|
-
console.error("[S3ClipsService] Error getting neighboring clips:", error);
|
|
4475
|
-
return { previous: null, next: null };
|
|
4476
|
-
}
|
|
4477
|
-
}
|
|
4478
|
-
/**
|
|
4479
|
-
* Cleanup
|
|
4480
|
-
*/
|
|
4481
|
-
dispose() {
|
|
4482
|
-
console.log("[S3ClipsService] Disposing service");
|
|
4483
|
-
this.apiClient.dispose();
|
|
4484
|
-
}
|
|
4485
|
-
/**
|
|
4486
|
-
* Get statistics
|
|
4487
|
-
*/
|
|
4488
|
-
getStats() {
|
|
4489
|
-
return this.apiClient.getStats();
|
|
4490
|
-
}
|
|
4491
|
-
};
|
|
4492
|
-
var getSupabaseClient2 = () => {
|
|
4493
|
-
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
4494
|
-
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
4495
|
-
if (!url || !key) {
|
|
4496
|
-
throw new Error("Supabase configuration missing");
|
|
4497
|
-
}
|
|
4498
|
-
return createClient(url, key);
|
|
4499
|
-
};
|
|
4500
|
-
var getAuthToken2 = async () => {
|
|
4501
|
-
try {
|
|
4502
|
-
const supabase = getSupabaseClient2();
|
|
4503
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
4504
3587
|
console.log("[S3ClipsSupabase] Auth session exists:", !!session, "has token:", !!session?.access_token);
|
|
4505
3588
|
return session?.access_token || null;
|
|
4506
3589
|
} catch (error) {
|
|
@@ -4531,7 +3614,7 @@ var S3ClipsSupabaseService = class {
|
|
|
4531
3614
|
* Fetch with authentication and error handling
|
|
4532
3615
|
*/
|
|
4533
3616
|
async fetchWithAuth(endpoint, body) {
|
|
4534
|
-
const token = await
|
|
3617
|
+
const token = await getAuthToken();
|
|
4535
3618
|
if (!token) {
|
|
4536
3619
|
throw new Error("Authentication required");
|
|
4537
3620
|
}
|
|
@@ -4743,21 +3826,60 @@ var S3ClipsSupabaseService = class {
|
|
|
4743
3826
|
return video || null;
|
|
4744
3827
|
});
|
|
4745
3828
|
}
|
|
3829
|
+
/**
|
|
3830
|
+
* Map percentile category to actual implementation
|
|
3831
|
+
*/
|
|
3832
|
+
mapPercentileCategoryToType(category) {
|
|
3833
|
+
switch (category) {
|
|
3834
|
+
case "fast-cycles":
|
|
3835
|
+
return { type: "fast-cycles", isPercentile: true };
|
|
3836
|
+
case "slow-cycles":
|
|
3837
|
+
return { type: "slow-cycles", isPercentile: true };
|
|
3838
|
+
case "longest-idles":
|
|
3839
|
+
return { type: "idle-times", isPercentile: true };
|
|
3840
|
+
default:
|
|
3841
|
+
return { type: null, isPercentile: false };
|
|
3842
|
+
}
|
|
3843
|
+
}
|
|
4746
3844
|
/**
|
|
4747
3845
|
* Get neighboring clips for navigation
|
|
4748
3846
|
* Returns previous and next clips based on timestamp
|
|
3847
|
+
* Handles both regular and percentile categories
|
|
4749
3848
|
*/
|
|
4750
3849
|
async getNeighboringClips(workspaceId, date, shiftId, category, currentClipId, sopCategories) {
|
|
4751
|
-
console.log(`[S3ClipsSupabase] Getting neighboring clips for ID: ${currentClipId}`);
|
|
4752
|
-
const
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
3850
|
+
console.log(`[S3ClipsSupabase] Getting neighboring clips for ID: ${currentClipId}, category: ${category}`);
|
|
3851
|
+
const percentileMapping = this.mapPercentileCategoryToType(category);
|
|
3852
|
+
if (percentileMapping.isPercentile && percentileMapping.type) {
|
|
3853
|
+
console.log(`[S3ClipsSupabase] Handling percentile category: ${category} -> ${percentileMapping.type}`);
|
|
3854
|
+
const percentileClips = await this.getPercentileClips(
|
|
3855
|
+
workspaceId,
|
|
3856
|
+
date,
|
|
3857
|
+
shiftId,
|
|
3858
|
+
percentileMapping.type,
|
|
3859
|
+
10,
|
|
3860
|
+
// Default percentile, should be configurable
|
|
3861
|
+
1e3
|
|
3862
|
+
// Large limit to get all clips for navigation
|
|
3863
|
+
);
|
|
3864
|
+
const currentIndex = percentileClips.clips.findIndex((clip) => clip.id === currentClipId);
|
|
3865
|
+
if (currentIndex === -1) {
|
|
3866
|
+
return { previous: null, next: null };
|
|
3867
|
+
}
|
|
3868
|
+
return {
|
|
3869
|
+
previous: currentIndex > 0 ? percentileClips.clips[currentIndex - 1] : null,
|
|
3870
|
+
next: currentIndex < percentileClips.clips.length - 1 ? percentileClips.clips[currentIndex + 1] : null
|
|
3871
|
+
};
|
|
3872
|
+
} else {
|
|
3873
|
+
const response = await this.fetchWithAuth("neighbors", {
|
|
3874
|
+
workspaceId,
|
|
3875
|
+
date,
|
|
3876
|
+
shift: shiftId.toString(),
|
|
3877
|
+
category,
|
|
3878
|
+
currentClipId,
|
|
3879
|
+
sopCategories: sopCategories || this.config.s3Config?.sopCategories?.default
|
|
3880
|
+
});
|
|
3881
|
+
return response.neighbors || { previous: null, next: null };
|
|
3882
|
+
}
|
|
4761
3883
|
}
|
|
4762
3884
|
/**
|
|
4763
3885
|
* Get clip by index
|
|
@@ -4867,8 +3989,96 @@ var S3ClipsSupabaseService = class {
|
|
|
4867
3989
|
await this.batchFetchClips(workspaceId, date, shiftId, requests);
|
|
4868
3990
|
}
|
|
4869
3991
|
}
|
|
3992
|
+
/**
|
|
3993
|
+
* Get percentile-based clips (fast cycles, slow cycles, idle times)
|
|
3994
|
+
*/
|
|
3995
|
+
async getPercentileClips(workspaceId, date, shiftId, type, percentile = 10, limit = 100) {
|
|
3996
|
+
const cacheKey = `percentile:${workspaceId}:${date}:${shiftId}:${type}:${percentile}`;
|
|
3997
|
+
return this.deduplicate(cacheKey, async () => {
|
|
3998
|
+
console.log(`[S3ClipsSupabase] Fetching ${type} clips at ${percentile}% percentile`);
|
|
3999
|
+
const startDate = `${date}T00:00:00Z`;
|
|
4000
|
+
const endDate = `${date}T23:59:59Z`;
|
|
4001
|
+
const response = await this.fetchWithAuth("percentile-clips", {
|
|
4002
|
+
action: type,
|
|
4003
|
+
workspaceId,
|
|
4004
|
+
startDate,
|
|
4005
|
+
endDate,
|
|
4006
|
+
percentile,
|
|
4007
|
+
shiftId: shiftId || null,
|
|
4008
|
+
limit
|
|
4009
|
+
});
|
|
4010
|
+
console.log(`[S3ClipsSupabase] Fetched ${response.clips?.length || 0} ${type} clips`);
|
|
4011
|
+
const transformedClips = (response.clips || []).map((clip) => ({
|
|
4012
|
+
id: clip.clip_id,
|
|
4013
|
+
src: clip.playlist,
|
|
4014
|
+
// Raw playlist content
|
|
4015
|
+
timestamp: new Date(clip.date).toLocaleTimeString("en-US", {
|
|
4016
|
+
hour12: false,
|
|
4017
|
+
hour: "2-digit",
|
|
4018
|
+
minute: "2-digit",
|
|
4019
|
+
second: "2-digit"
|
|
4020
|
+
}),
|
|
4021
|
+
severity: this.getSeverityFromClipType(clip.clip_type_name),
|
|
4022
|
+
description: this.getDescriptionFromClipType(clip.clip_type_name),
|
|
4023
|
+
type: clip.clip_type_name,
|
|
4024
|
+
originalUri: `clips:${clip.clip_id}`,
|
|
4025
|
+
cycle_time_seconds: clip.metadata?.request?.metadata?.cycle_time ? clip.metadata.request.metadata.cycle_time / 20 : void 0,
|
|
4026
|
+
creation_timestamp: clip.created_at,
|
|
4027
|
+
percentile: clip.cycle_time_percentile || clip.idle_time_percentile
|
|
4028
|
+
}));
|
|
4029
|
+
return {
|
|
4030
|
+
clips: transformedClips,
|
|
4031
|
+
total: response.total || transformedClips.length,
|
|
4032
|
+
hasMore: response.hasMore || false
|
|
4033
|
+
};
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
4036
|
+
/**
|
|
4037
|
+
* Get workspace statistics
|
|
4038
|
+
*/
|
|
4039
|
+
async getWorkspaceStats(workspaceId, startDate, endDate) {
|
|
4040
|
+
const cacheKey = `stats:${workspaceId}:${startDate || "all"}:${endDate || "all"}`;
|
|
4041
|
+
return this.deduplicate(cacheKey, async () => {
|
|
4042
|
+
console.log(`[S3ClipsSupabase] Fetching workspace statistics`);
|
|
4043
|
+
const response = await this.fetchWithAuth("workspace-stats", {
|
|
4044
|
+
workspaceId,
|
|
4045
|
+
startDate,
|
|
4046
|
+
endDate
|
|
4047
|
+
});
|
|
4048
|
+
console.log(`[S3ClipsSupabase] Fetched workspace statistics`, response.stats);
|
|
4049
|
+
return response.stats;
|
|
4050
|
+
});
|
|
4051
|
+
}
|
|
4052
|
+
/**
|
|
4053
|
+
* Helper to get severity from clip type
|
|
4054
|
+
* Updated for new materialized view structure (only cycle_completion in 'cycle' category)
|
|
4055
|
+
*/
|
|
4056
|
+
getSeverityFromClipType(clipType) {
|
|
4057
|
+
switch (clipType) {
|
|
4058
|
+
case "sop_deviations":
|
|
4059
|
+
return "high";
|
|
4060
|
+
case "idle_time":
|
|
4061
|
+
return "medium";
|
|
4062
|
+
case "cycle_completion":
|
|
4063
|
+
default:
|
|
4064
|
+
return "low";
|
|
4065
|
+
}
|
|
4066
|
+
}
|
|
4067
|
+
/**
|
|
4068
|
+
* Helper to get description from clip type
|
|
4069
|
+
* Updated for new materialized view structure
|
|
4070
|
+
*/
|
|
4071
|
+
getDescriptionFromClipType(clipType) {
|
|
4072
|
+
const descriptions = {
|
|
4073
|
+
"idle_time": "Idle Time Detected",
|
|
4074
|
+
"sop_deviations": "SOP Deviations",
|
|
4075
|
+
"cycle_completion": "Cycle Completion"
|
|
4076
|
+
};
|
|
4077
|
+
return descriptions[clipType] || "Analysis Clip";
|
|
4078
|
+
}
|
|
4870
4079
|
};
|
|
4871
|
-
|
|
4080
|
+
|
|
4081
|
+
// src/lib/services/videoPrefetchManager.ts
|
|
4872
4082
|
var VideoPrefetchManager = class extends EventEmitter {
|
|
4873
4083
|
constructor() {
|
|
4874
4084
|
super();
|
|
@@ -4882,7 +4092,7 @@ var VideoPrefetchManager = class extends EventEmitter {
|
|
|
4882
4092
|
getS3Service(dashboardConfig) {
|
|
4883
4093
|
const configKey = JSON.stringify(dashboardConfig.s3Config);
|
|
4884
4094
|
if (!this.s3Services.has(configKey)) {
|
|
4885
|
-
this.s3Services.set(configKey, new
|
|
4095
|
+
this.s3Services.set(configKey, new S3ClipsSupabaseService(dashboardConfig));
|
|
4886
4096
|
}
|
|
4887
4097
|
return this.s3Services.get(configKey);
|
|
4888
4098
|
}
|
|
@@ -5702,6 +4912,95 @@ var useSubscriptionManagerSafe = () => {
|
|
|
5702
4912
|
const { subscriptionManager } = useContext(SubscriptionManagerContext);
|
|
5703
4913
|
return subscriptionManager;
|
|
5704
4914
|
};
|
|
4915
|
+
var defaultState = {
|
|
4916
|
+
percentile: 10,
|
|
4917
|
+
showFastCycles: true,
|
|
4918
|
+
showSlowCycles: true,
|
|
4919
|
+
showLongestIdles: true,
|
|
4920
|
+
showIdleTime: true,
|
|
4921
|
+
showCycleCompletion: true,
|
|
4922
|
+
showBestCycleTime: false,
|
|
4923
|
+
showWorstCycleTime: false,
|
|
4924
|
+
showLongCycleTime: false,
|
|
4925
|
+
showSopDeviations: false,
|
|
4926
|
+
dateRange: {
|
|
4927
|
+
start: /* @__PURE__ */ new Date(),
|
|
4928
|
+
end: /* @__PURE__ */ new Date()
|
|
4929
|
+
},
|
|
4930
|
+
shiftFilter: void 0,
|
|
4931
|
+
percentileRange: void 0,
|
|
4932
|
+
isAdvancedPanelOpen: false,
|
|
4933
|
+
isLoadingPercentileClips: false
|
|
4934
|
+
};
|
|
4935
|
+
var ClipFilterContext = createContext(void 0);
|
|
4936
|
+
var ClipFilterProvider = ({ children }) => {
|
|
4937
|
+
const [state, setState] = useState(defaultState);
|
|
4938
|
+
const updatePercentile = useCallback((percentile) => {
|
|
4939
|
+
if (percentile >= 1 && percentile <= 100) {
|
|
4940
|
+
setState((prev) => ({ ...prev, percentile }));
|
|
4941
|
+
}
|
|
4942
|
+
}, []);
|
|
4943
|
+
const toggleClipType = useCallback((clipType) => {
|
|
4944
|
+
setState((prev) => ({
|
|
4945
|
+
...prev,
|
|
4946
|
+
[clipType]: !prev[clipType]
|
|
4947
|
+
}));
|
|
4948
|
+
}, []);
|
|
4949
|
+
const updateDateRange = useCallback((start, end) => {
|
|
4950
|
+
setState((prev) => ({
|
|
4951
|
+
...prev,
|
|
4952
|
+
dateRange: { start, end }
|
|
4953
|
+
}));
|
|
4954
|
+
}, []);
|
|
4955
|
+
const updateShiftFilter = useCallback((shiftId) => {
|
|
4956
|
+
setState((prev) => ({
|
|
4957
|
+
...prev,
|
|
4958
|
+
shiftFilter: shiftId
|
|
4959
|
+
}));
|
|
4960
|
+
}, []);
|
|
4961
|
+
const setPercentileRange = useCallback((min, max) => {
|
|
4962
|
+
if (min >= 0 && max <= 100 && min < max) {
|
|
4963
|
+
setState((prev) => ({
|
|
4964
|
+
...prev,
|
|
4965
|
+
percentileRange: { min, max }
|
|
4966
|
+
}));
|
|
4967
|
+
}
|
|
4968
|
+
}, []);
|
|
4969
|
+
const toggleAdvancedPanel = useCallback(() => {
|
|
4970
|
+
setState((prev) => ({
|
|
4971
|
+
...prev,
|
|
4972
|
+
isAdvancedPanelOpen: !prev.isAdvancedPanelOpen
|
|
4973
|
+
}));
|
|
4974
|
+
}, []);
|
|
4975
|
+
const setLoadingPercentileClips = useCallback((loading) => {
|
|
4976
|
+
setState((prev) => ({
|
|
4977
|
+
...prev,
|
|
4978
|
+
isLoadingPercentileClips: loading
|
|
4979
|
+
}));
|
|
4980
|
+
}, []);
|
|
4981
|
+
const resetFilters = useCallback(() => {
|
|
4982
|
+
setState(defaultState);
|
|
4983
|
+
}, []);
|
|
4984
|
+
const value = {
|
|
4985
|
+
state,
|
|
4986
|
+
updatePercentile,
|
|
4987
|
+
toggleClipType,
|
|
4988
|
+
updateDateRange,
|
|
4989
|
+
updateShiftFilter,
|
|
4990
|
+
setPercentileRange,
|
|
4991
|
+
toggleAdvancedPanel,
|
|
4992
|
+
resetFilters,
|
|
4993
|
+
setLoadingPercentileClips
|
|
4994
|
+
};
|
|
4995
|
+
return /* @__PURE__ */ jsx(ClipFilterContext.Provider, { value, children });
|
|
4996
|
+
};
|
|
4997
|
+
var useClipFilter = () => {
|
|
4998
|
+
const context = useContext(ClipFilterContext);
|
|
4999
|
+
if (!context) {
|
|
5000
|
+
throw new Error("useClipFilter must be used within ClipFilterProvider");
|
|
5001
|
+
}
|
|
5002
|
+
return context;
|
|
5003
|
+
};
|
|
5705
5004
|
var DEFAULT_COMPANY_ID = "default-company-id";
|
|
5706
5005
|
var useWorkspaceMetrics = (workspaceId) => {
|
|
5707
5006
|
const supabase = useSupabase();
|
|
@@ -12932,7 +12231,7 @@ var MotionConfigContext = createContext({
|
|
|
12932
12231
|
});
|
|
12933
12232
|
|
|
12934
12233
|
// ../../node_modules/framer-motion/dist/es/components/AnimatePresence/PopChild.mjs
|
|
12935
|
-
var PopChildMeasure = class extends
|
|
12234
|
+
var PopChildMeasure = class extends React20.Component {
|
|
12936
12235
|
getSnapshotBeforeUpdate(prevProps) {
|
|
12937
12236
|
const element = this.props.childRef.current;
|
|
12938
12237
|
if (element && prevProps.isPresent && !this.props.isPresent) {
|
|
@@ -12987,7 +12286,7 @@ function PopChild({ children, isPresent }) {
|
|
|
12987
12286
|
document.head.removeChild(style);
|
|
12988
12287
|
};
|
|
12989
12288
|
}, [isPresent]);
|
|
12990
|
-
return jsx(PopChildMeasure, { isPresent, childRef: ref, sizeRef: size, children:
|
|
12289
|
+
return jsx(PopChildMeasure, { isPresent, childRef: ref, sizeRef: size, children: React20.cloneElement(children, { ref }) });
|
|
12991
12290
|
}
|
|
12992
12291
|
|
|
12993
12292
|
// ../../node_modules/framer-motion/dist/es/components/AnimatePresence/PresenceChild.mjs
|
|
@@ -13024,7 +12323,7 @@ var PresenceChild = ({ children, initial, isPresent, onExitComplete, custom, pre
|
|
|
13024
12323
|
useMemo(() => {
|
|
13025
12324
|
presenceChildren.forEach((_, key) => presenceChildren.set(key, false));
|
|
13026
12325
|
}, [isPresent]);
|
|
13027
|
-
|
|
12326
|
+
React20.useEffect(() => {
|
|
13028
12327
|
!isPresent && !presenceChildren.size && onExitComplete && onExitComplete();
|
|
13029
12328
|
}, [isPresent]);
|
|
13030
12329
|
if (mode === "popLayout") {
|
|
@@ -20308,7 +19607,7 @@ var LoadingPage = ({
|
|
|
20308
19607
|
subMessage = "Please wait while we prepare your data",
|
|
20309
19608
|
className
|
|
20310
19609
|
}) => {
|
|
20311
|
-
|
|
19610
|
+
React20__default.useEffect(() => {
|
|
20312
19611
|
console.log("LoadingPage rendered with message:", message);
|
|
20313
19612
|
const timeout = setTimeout(() => {
|
|
20314
19613
|
console.warn("LoadingPage has been visible for more than 8 seconds. This might indicate an issue.");
|
|
@@ -20349,15 +19648,15 @@ var withAuth = (WrappedComponent2, options) => {
|
|
|
20349
19648
|
requireAuth: true,
|
|
20350
19649
|
...options
|
|
20351
19650
|
};
|
|
20352
|
-
const WithAuthComponent =
|
|
19651
|
+
const WithAuthComponent = React20.memo(function WithAuthComponent2(props) {
|
|
20353
19652
|
const { session, loading, error } = useAuth();
|
|
20354
19653
|
const router = useRouter();
|
|
20355
|
-
|
|
19654
|
+
React20.useEffect(() => {
|
|
20356
19655
|
if (process.env.NODE_ENV === "development" && process.env.DEBUG_AUTH === "true") {
|
|
20357
19656
|
console.log("withAuth state:", { loading, hasSession: !!session, requireAuth: defaultOptions.requireAuth });
|
|
20358
19657
|
}
|
|
20359
19658
|
}, [session, loading]);
|
|
20360
|
-
|
|
19659
|
+
React20.useEffect(() => {
|
|
20361
19660
|
if (!loading && defaultOptions.requireAuth && !session && !error) {
|
|
20362
19661
|
console.log("Redirecting to login from withAuth");
|
|
20363
19662
|
router.replace(defaultOptions.redirectTo);
|
|
@@ -20733,11 +20032,11 @@ var BarChartComponent = ({
|
|
|
20733
20032
|
aspect = 2,
|
|
20734
20033
|
...restOfChartProps
|
|
20735
20034
|
}) => {
|
|
20736
|
-
const containerRef =
|
|
20737
|
-
const [containerReady, setContainerReady] =
|
|
20035
|
+
const containerRef = React20__default.useRef(null);
|
|
20036
|
+
const [containerReady, setContainerReady] = React20__default.useState(false);
|
|
20738
20037
|
const themeConfig = useThemeConfig();
|
|
20739
20038
|
const { formatNumber } = useFormatNumber();
|
|
20740
|
-
|
|
20039
|
+
React20__default.useEffect(() => {
|
|
20741
20040
|
const checkContainerDimensions = () => {
|
|
20742
20041
|
if (containerRef.current) {
|
|
20743
20042
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -20835,7 +20134,7 @@ var BarChartComponent = ({
|
|
|
20835
20134
|
}
|
|
20836
20135
|
return /* @__PURE__ */ jsx("div", { className: clsx("w-full", className), children: chartContent });
|
|
20837
20136
|
};
|
|
20838
|
-
var BarChart =
|
|
20137
|
+
var BarChart = React20__default.memo(BarChartComponent, (prevProps, nextProps) => {
|
|
20839
20138
|
if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || prevProps.layout !== nextProps.layout || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect) {
|
|
20840
20139
|
return false;
|
|
20841
20140
|
}
|
|
@@ -20883,11 +20182,11 @@ var LineChartComponent = ({
|
|
|
20883
20182
|
aspect = 2,
|
|
20884
20183
|
...restOfChartProps
|
|
20885
20184
|
}) => {
|
|
20886
|
-
const containerRef =
|
|
20887
|
-
const [containerReady, setContainerReady] =
|
|
20185
|
+
const containerRef = React20__default.useRef(null);
|
|
20186
|
+
const [containerReady, setContainerReady] = React20__default.useState(false);
|
|
20888
20187
|
const themeConfig = useThemeConfig();
|
|
20889
20188
|
const { formatNumber } = useFormatNumber();
|
|
20890
|
-
|
|
20189
|
+
React20__default.useEffect(() => {
|
|
20891
20190
|
const checkContainerDimensions = () => {
|
|
20892
20191
|
if (containerRef.current) {
|
|
20893
20192
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -20986,7 +20285,7 @@ var LineChartComponent = ({
|
|
|
20986
20285
|
}
|
|
20987
20286
|
return /* @__PURE__ */ jsx("div", { className: clsx("w-full", className), children: chartContent });
|
|
20988
20287
|
};
|
|
20989
|
-
var LineChart =
|
|
20288
|
+
var LineChart = React20__default.memo(LineChartComponent, (prevProps, nextProps) => {
|
|
20990
20289
|
if (prevProps.xAxisDataKey !== nextProps.xAxisDataKey || prevProps.xAxisLabel !== nextProps.xAxisLabel || prevProps.yAxisLabel !== nextProps.yAxisLabel || prevProps.yAxisUnit !== nextProps.yAxisUnit || prevProps.className !== nextProps.className || prevProps.showGrid !== nextProps.showGrid || prevProps.showLegend !== nextProps.showLegend || prevProps.showTooltip !== nextProps.showTooltip || prevProps.responsive !== nextProps.responsive || prevProps.aspect !== nextProps.aspect || JSON.stringify(prevProps.yAxisDomain) !== JSON.stringify(nextProps.yAxisDomain)) {
|
|
20991
20290
|
return false;
|
|
20992
20291
|
}
|
|
@@ -21063,7 +20362,7 @@ var OutputProgressChartComponent = ({
|
|
|
21063
20362
|
] }) })
|
|
21064
20363
|
] }) });
|
|
21065
20364
|
};
|
|
21066
|
-
var OutputProgressChart =
|
|
20365
|
+
var OutputProgressChart = React20__default.memo(OutputProgressChartComponent);
|
|
21067
20366
|
OutputProgressChart.displayName = "OutputProgressChart";
|
|
21068
20367
|
var LargeOutputProgressChart = ({
|
|
21069
20368
|
currentOutput,
|
|
@@ -21179,7 +20478,7 @@ var CycleTimeChartComponent = ({
|
|
|
21179
20478
|
}
|
|
21180
20479
|
) }) });
|
|
21181
20480
|
};
|
|
21182
|
-
var CycleTimeChart =
|
|
20481
|
+
var CycleTimeChart = React20__default.memo(CycleTimeChartComponent, (prevProps, nextProps) => {
|
|
21183
20482
|
if (prevProps.className !== nextProps.className) {
|
|
21184
20483
|
return false;
|
|
21185
20484
|
}
|
|
@@ -21205,8 +20504,8 @@ var CycleTimeOverTimeChart = ({
|
|
|
21205
20504
|
className = ""
|
|
21206
20505
|
}) => {
|
|
21207
20506
|
const MAX_DATA_POINTS = 40;
|
|
21208
|
-
const containerRef =
|
|
21209
|
-
const [containerReady, setContainerReady] =
|
|
20507
|
+
const containerRef = React20__default.useRef(null);
|
|
20508
|
+
const [containerReady, setContainerReady] = React20__default.useState(false);
|
|
21210
20509
|
const getHourFromTimeString = (timeStr) => {
|
|
21211
20510
|
const [hours, minutes] = timeStr.split(":");
|
|
21212
20511
|
return parseInt(hours);
|
|
@@ -21217,10 +20516,10 @@ var CycleTimeOverTimeChart = ({
|
|
|
21217
20516
|
};
|
|
21218
20517
|
const displayData = getDisplayData(data);
|
|
21219
20518
|
const DURATION = displayData.length;
|
|
21220
|
-
const [animatedData, setAnimatedData] =
|
|
21221
|
-
const prevDataRef =
|
|
21222
|
-
const animationFrameRef =
|
|
21223
|
-
const animateToNewData =
|
|
20519
|
+
const [animatedData, setAnimatedData] = React20__default.useState(Array(DURATION).fill(0));
|
|
20520
|
+
const prevDataRef = React20__default.useRef(Array(DURATION).fill(0));
|
|
20521
|
+
const animationFrameRef = React20__default.useRef(null);
|
|
20522
|
+
const animateToNewData = React20__default.useCallback((targetData) => {
|
|
21224
20523
|
const startData = [...prevDataRef.current];
|
|
21225
20524
|
const startTime = performance.now();
|
|
21226
20525
|
const duration = 1200;
|
|
@@ -21250,7 +20549,7 @@ var CycleTimeOverTimeChart = ({
|
|
|
21250
20549
|
}
|
|
21251
20550
|
animationFrameRef.current = requestAnimationFrame(animate);
|
|
21252
20551
|
}, []);
|
|
21253
|
-
|
|
20552
|
+
React20__default.useEffect(() => {
|
|
21254
20553
|
if (JSON.stringify(data) !== JSON.stringify(prevDataRef.current)) {
|
|
21255
20554
|
const processedData = getDisplayData(data);
|
|
21256
20555
|
animateToNewData(processedData);
|
|
@@ -21261,7 +20560,7 @@ var CycleTimeOverTimeChart = ({
|
|
|
21261
20560
|
}
|
|
21262
20561
|
};
|
|
21263
20562
|
}, [data, animateToNewData]);
|
|
21264
|
-
|
|
20563
|
+
React20__default.useEffect(() => {
|
|
21265
20564
|
const checkContainerDimensions = () => {
|
|
21266
20565
|
if (containerRef.current) {
|
|
21267
20566
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -21504,7 +20803,7 @@ var CycleTimeOverTimeChart = ({
|
|
|
21504
20803
|
}
|
|
21505
20804
|
);
|
|
21506
20805
|
};
|
|
21507
|
-
var Card =
|
|
20806
|
+
var Card = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21508
20807
|
"div",
|
|
21509
20808
|
{
|
|
21510
20809
|
ref,
|
|
@@ -21516,7 +20815,7 @@ var Card = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
21516
20815
|
}
|
|
21517
20816
|
));
|
|
21518
20817
|
Card.displayName = "Card";
|
|
21519
|
-
var CardHeader =
|
|
20818
|
+
var CardHeader = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21520
20819
|
"div",
|
|
21521
20820
|
{
|
|
21522
20821
|
ref,
|
|
@@ -21525,7 +20824,7 @@ var CardHeader = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE
|
|
|
21525
20824
|
}
|
|
21526
20825
|
));
|
|
21527
20826
|
CardHeader.displayName = "CardHeader";
|
|
21528
|
-
var CardTitle =
|
|
20827
|
+
var CardTitle = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21529
20828
|
"h3",
|
|
21530
20829
|
{
|
|
21531
20830
|
ref,
|
|
@@ -21537,7 +20836,7 @@ var CardTitle = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE_
|
|
|
21537
20836
|
}
|
|
21538
20837
|
));
|
|
21539
20838
|
CardTitle.displayName = "CardTitle";
|
|
21540
|
-
var CardDescription =
|
|
20839
|
+
var CardDescription = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21541
20840
|
"p",
|
|
21542
20841
|
{
|
|
21543
20842
|
ref,
|
|
@@ -21546,9 +20845,9 @@ var CardDescription = React19.forwardRef(({ className, ...props }, ref) => /* @_
|
|
|
21546
20845
|
}
|
|
21547
20846
|
));
|
|
21548
20847
|
CardDescription.displayName = "CardDescription";
|
|
21549
|
-
var CardContent =
|
|
20848
|
+
var CardContent = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
21550
20849
|
CardContent.displayName = "CardContent";
|
|
21551
|
-
var CardFooter =
|
|
20850
|
+
var CardFooter = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
21552
20851
|
"div",
|
|
21553
20852
|
{
|
|
21554
20853
|
ref,
|
|
@@ -21624,7 +20923,7 @@ var buttonVariants = cva(
|
|
|
21624
20923
|
}
|
|
21625
20924
|
}
|
|
21626
20925
|
);
|
|
21627
|
-
var Button =
|
|
20926
|
+
var Button = React20.forwardRef(
|
|
21628
20927
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
21629
20928
|
const Comp = asChild ? Slot : "button";
|
|
21630
20929
|
return /* @__PURE__ */ jsx(
|
|
@@ -21647,8 +20946,8 @@ var HourlyOutputChartComponent = ({
|
|
|
21647
20946
|
idleTimeHourly,
|
|
21648
20947
|
className = ""
|
|
21649
20948
|
}) => {
|
|
21650
|
-
const containerRef =
|
|
21651
|
-
const [containerReady, setContainerReady] =
|
|
20949
|
+
const containerRef = React20__default.useRef(null);
|
|
20950
|
+
const [containerReady, setContainerReady] = React20__default.useState(false);
|
|
21652
20951
|
const getTimeFromTimeString = (timeStr) => {
|
|
21653
20952
|
const [hours, minutes] = timeStr.split(":");
|
|
21654
20953
|
const hour = parseInt(hours);
|
|
@@ -21657,7 +20956,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21657
20956
|
return { hour, minute, decimalHour };
|
|
21658
20957
|
};
|
|
21659
20958
|
const shiftStartTime = getTimeFromTimeString(shiftStart);
|
|
21660
|
-
const { shiftDuration, shiftEndTime, hasPartialLastHour } =
|
|
20959
|
+
const { shiftDuration, shiftEndTime, hasPartialLastHour } = React20__default.useMemo(() => {
|
|
21661
20960
|
console.log("[HourlyOutputChart] Calculating shift duration with:", {
|
|
21662
20961
|
shiftStart,
|
|
21663
20962
|
shiftEnd,
|
|
@@ -21692,12 +20991,12 @@ var HourlyOutputChartComponent = ({
|
|
|
21692
20991
|
}, [shiftEnd, shiftStartTime.decimalHour]);
|
|
21693
20992
|
const SHIFT_DURATION = shiftDuration;
|
|
21694
20993
|
shiftEndTime ? shiftEndTime.hour : (shiftStartTime.hour + SHIFT_DURATION) % 24;
|
|
21695
|
-
const [animatedData, setAnimatedData] =
|
|
20994
|
+
const [animatedData, setAnimatedData] = React20__default.useState(
|
|
21696
20995
|
() => Array(SHIFT_DURATION).fill(0)
|
|
21697
20996
|
);
|
|
21698
|
-
const prevDataRef =
|
|
21699
|
-
const animationFrameRef =
|
|
21700
|
-
|
|
20997
|
+
const prevDataRef = React20__default.useRef(Array(SHIFT_DURATION).fill(0));
|
|
20998
|
+
const animationFrameRef = React20__default.useRef(null);
|
|
20999
|
+
React20__default.useEffect(() => {
|
|
21701
21000
|
setAnimatedData((prev) => {
|
|
21702
21001
|
if (prev.length !== SHIFT_DURATION) {
|
|
21703
21002
|
return Array(SHIFT_DURATION).fill(0);
|
|
@@ -21706,14 +21005,14 @@ var HourlyOutputChartComponent = ({
|
|
|
21706
21005
|
});
|
|
21707
21006
|
prevDataRef.current = Array(SHIFT_DURATION).fill(0);
|
|
21708
21007
|
}, [SHIFT_DURATION]);
|
|
21709
|
-
const [idleBarState, setIdleBarState] =
|
|
21008
|
+
const [idleBarState, setIdleBarState] = React20__default.useState({
|
|
21710
21009
|
visible: showIdleTime,
|
|
21711
21010
|
key: 0,
|
|
21712
21011
|
shouldAnimate: false
|
|
21713
21012
|
});
|
|
21714
|
-
const prevShowIdleTimeRef =
|
|
21715
|
-
const stateUpdateTimeoutRef =
|
|
21716
|
-
|
|
21013
|
+
const prevShowIdleTimeRef = React20__default.useRef(showIdleTime);
|
|
21014
|
+
const stateUpdateTimeoutRef = React20__default.useRef(null);
|
|
21015
|
+
React20__default.useEffect(() => {
|
|
21717
21016
|
if (stateUpdateTimeoutRef.current) {
|
|
21718
21017
|
clearTimeout(stateUpdateTimeoutRef.current);
|
|
21719
21018
|
}
|
|
@@ -21738,7 +21037,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21738
21037
|
}
|
|
21739
21038
|
};
|
|
21740
21039
|
}, [showIdleTime]);
|
|
21741
|
-
const animateToNewData =
|
|
21040
|
+
const animateToNewData = React20__default.useCallback((targetData) => {
|
|
21742
21041
|
const startData = [...prevDataRef.current];
|
|
21743
21042
|
const startTime = performance.now();
|
|
21744
21043
|
const duration = 1200;
|
|
@@ -21768,7 +21067,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21768
21067
|
}
|
|
21769
21068
|
animationFrameRef.current = requestAnimationFrame(animate);
|
|
21770
21069
|
}, []);
|
|
21771
|
-
|
|
21070
|
+
React20__default.useEffect(() => {
|
|
21772
21071
|
if (JSON.stringify(data) !== JSON.stringify(prevDataRef.current)) {
|
|
21773
21072
|
const shiftData = data.slice(0, SHIFT_DURATION);
|
|
21774
21073
|
animateToNewData(shiftData);
|
|
@@ -21779,7 +21078,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21779
21078
|
}
|
|
21780
21079
|
};
|
|
21781
21080
|
}, [data, animateToNewData]);
|
|
21782
|
-
|
|
21081
|
+
React20__default.useEffect(() => {
|
|
21783
21082
|
const checkContainerDimensions = () => {
|
|
21784
21083
|
if (containerRef.current) {
|
|
21785
21084
|
const rect = containerRef.current.getBoundingClientRect();
|
|
@@ -21801,7 +21100,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21801
21100
|
clearTimeout(fallbackTimeout);
|
|
21802
21101
|
};
|
|
21803
21102
|
}, []);
|
|
21804
|
-
const formatHour =
|
|
21103
|
+
const formatHour = React20__default.useCallback((hourIndex) => {
|
|
21805
21104
|
const isLastHour = hourIndex === SHIFT_DURATION - 1;
|
|
21806
21105
|
const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
|
|
21807
21106
|
const startHour = Math.floor(startDecimalHour) % 24;
|
|
@@ -21825,7 +21124,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21825
21124
|
};
|
|
21826
21125
|
return `${formatTime3(startHour, startMinute)}-${formatTime3(endHour, endMinute)}`;
|
|
21827
21126
|
}, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
|
|
21828
|
-
const formatTimeRange =
|
|
21127
|
+
const formatTimeRange = React20__default.useCallback((hourIndex) => {
|
|
21829
21128
|
const isLastHour = hourIndex === SHIFT_DURATION - 1;
|
|
21830
21129
|
const startDecimalHour = shiftStartTime.decimalHour + hourIndex;
|
|
21831
21130
|
const startHour = Math.floor(startDecimalHour) % 24;
|
|
@@ -21846,7 +21145,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21846
21145
|
};
|
|
21847
21146
|
return `${formatTime3(startHour, startMinute)} - ${formatTime3(endHour, endMinute)}`;
|
|
21848
21147
|
}, [shiftStartTime.decimalHour, SHIFT_DURATION, shiftEndTime]);
|
|
21849
|
-
const chartData =
|
|
21148
|
+
const chartData = React20__default.useMemo(() => {
|
|
21850
21149
|
return Array.from({ length: SHIFT_DURATION }, (_, i) => {
|
|
21851
21150
|
const actualHour = (shiftStartTime.hour + i) % 24;
|
|
21852
21151
|
const startMinute = shiftStartTime.minute;
|
|
@@ -21915,7 +21214,7 @@ var HourlyOutputChartComponent = ({
|
|
|
21915
21214
|
};
|
|
21916
21215
|
});
|
|
21917
21216
|
}, [animatedData, data, pphThreshold, idleTimeHourly, shiftStartTime.hour, shiftStartTime.minute, shiftEndTime, formatHour, formatTimeRange, SHIFT_DURATION]);
|
|
21918
|
-
const IdleBar =
|
|
21217
|
+
const IdleBar = React20__default.useMemo(() => {
|
|
21919
21218
|
if (!idleBarState.visible) return null;
|
|
21920
21219
|
return /* @__PURE__ */ jsx(
|
|
21921
21220
|
Bar,
|
|
@@ -22240,7 +21539,7 @@ var HourlyOutputChartComponent = ({
|
|
|
22240
21539
|
}
|
|
22241
21540
|
);
|
|
22242
21541
|
};
|
|
22243
|
-
var HourlyOutputChart =
|
|
21542
|
+
var HourlyOutputChart = React20__default.memo(HourlyOutputChartComponent, (prevProps, nextProps) => {
|
|
22244
21543
|
if (prevProps.pphThreshold !== nextProps.pphThreshold || prevProps.shiftStart !== nextProps.shiftStart || prevProps.showIdleTime !== nextProps.showIdleTime || prevProps.className !== nextProps.className) {
|
|
22245
21544
|
return false;
|
|
22246
21545
|
}
|
|
@@ -22276,7 +21575,7 @@ function getTrendArrowAndColor(trend) {
|
|
|
22276
21575
|
return { arrow: "\u2192", color: "text-gray-400" };
|
|
22277
21576
|
}
|
|
22278
21577
|
}
|
|
22279
|
-
var VideoCard =
|
|
21578
|
+
var VideoCard = React20__default.memo(({
|
|
22280
21579
|
workspace,
|
|
22281
21580
|
hlsUrl,
|
|
22282
21581
|
shouldPlay,
|
|
@@ -22425,7 +21724,7 @@ var VideoCard = React19__default.memo(({
|
|
|
22425
21724
|
});
|
|
22426
21725
|
VideoCard.displayName = "VideoCard";
|
|
22427
21726
|
var DEFAULT_HLS_URL = "https://192.168.5.9:8443/cam1.m3u8";
|
|
22428
|
-
var VideoGridView =
|
|
21727
|
+
var VideoGridView = React20__default.memo(({
|
|
22429
21728
|
workspaces,
|
|
22430
21729
|
selectedLine,
|
|
22431
21730
|
className = "",
|
|
@@ -22578,32 +21877,10 @@ var VideoGridView = React19__default.memo(({
|
|
|
22578
21877
|
observerRef.current?.disconnect();
|
|
22579
21878
|
};
|
|
22580
21879
|
}, [filteredWorkspaces]);
|
|
22581
|
-
|
|
21880
|
+
useRef({});
|
|
22582
21881
|
const handleWorkspaceClick = useCallback((workspace) => {
|
|
22583
21882
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
22584
|
-
|
|
22585
|
-
const operationalDate = getOperationalDate(dashboardConfig.dateTimeConfig?.defaultTimezone);
|
|
22586
|
-
const fullKey = `${workspaceId}-${operationalDate}-all-all-meta-1000--`;
|
|
22587
|
-
if (!prefetchCacheRef.current[fullKey]?.status) {
|
|
22588
|
-
const clipsService = new S3ClipsService(dashboardConfig);
|
|
22589
|
-
const fullPromise = clipsService.fetchClips({
|
|
22590
|
-
workspaceId,
|
|
22591
|
-
date: operationalDate,
|
|
22592
|
-
mode: "full",
|
|
22593
|
-
includeCycleTime: true,
|
|
22594
|
-
includeMetadata: true,
|
|
22595
|
-
limit: 1e3
|
|
22596
|
-
});
|
|
22597
|
-
prefetchCacheRef.current[fullKey] = { status: "pending", promise: fullPromise };
|
|
22598
|
-
fullPromise.then((data) => {
|
|
22599
|
-
prefetchCacheRef.current[fullKey] = { status: "resolved", data };
|
|
22600
|
-
console.log(`Prefetched full clips data for workspace ${workspaceId}`);
|
|
22601
|
-
}).catch((error) => {
|
|
22602
|
-
prefetchCacheRef.current[fullKey] = { status: "rejected", error };
|
|
22603
|
-
console.warn(`Failed to prefetch full clips for workspace ${workspaceId}:`, error);
|
|
22604
|
-
});
|
|
22605
|
-
}
|
|
22606
|
-
}
|
|
21883
|
+
console.log(`[VideoGridView] Prefetching disabled for workspace ${workspaceId}`);
|
|
22607
21884
|
trackCoreEvent("Workspace Detail Clicked", {
|
|
22608
21885
|
workspace_name: workspace.workspace_name,
|
|
22609
21886
|
workspace_id: workspaceId,
|
|
@@ -23203,7 +22480,7 @@ var PieChart4 = ({
|
|
|
23203
22480
|
}
|
|
23204
22481
|
);
|
|
23205
22482
|
};
|
|
23206
|
-
const
|
|
22483
|
+
const CustomTooltip2 = ({ active, payload }) => {
|
|
23207
22484
|
if (active && payload && payload.length) {
|
|
23208
22485
|
const data2 = payload[0];
|
|
23209
22486
|
return /* @__PURE__ */ jsxs("div", { className: "bg-white px-3 py-2 shadow-lg rounded-lg border border-gray-200", children: [
|
|
@@ -23234,7 +22511,7 @@ var PieChart4 = ({
|
|
|
23234
22511
|
children: dataWithPercentage.map((entry, index) => /* @__PURE__ */ jsx(Cell, { fill: colors[index % colors.length] }, `cell-${index}`))
|
|
23235
22512
|
}
|
|
23236
22513
|
),
|
|
23237
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(
|
|
22514
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip2, {}) }),
|
|
23238
22515
|
/* @__PURE__ */ jsx(
|
|
23239
22516
|
Legend,
|
|
23240
22517
|
{
|
|
@@ -23362,7 +22639,7 @@ var EmptyStateMessage = ({
|
|
|
23362
22639
|
iconClassName
|
|
23363
22640
|
}) => {
|
|
23364
22641
|
let IconContent = null;
|
|
23365
|
-
if (
|
|
22642
|
+
if (React20__default.isValidElement(iconType)) {
|
|
23366
22643
|
IconContent = iconType;
|
|
23367
22644
|
} else if (typeof iconType === "string") {
|
|
23368
22645
|
const MappedIcon = IconMap[iconType];
|
|
@@ -26365,6 +25642,28 @@ var getOrdinal = (n) => {
|
|
|
26365
25642
|
const v = n % 100;
|
|
26366
25643
|
return n + (suffix[(v - 20) % 10] || suffix[v] || suffix[0]);
|
|
26367
25644
|
};
|
|
25645
|
+
var CustomTooltip = ({ active, payload, label }) => {
|
|
25646
|
+
if (active && payload && payload.length) {
|
|
25647
|
+
return /* @__PURE__ */ jsxs("div", { className: "bg-white/95 backdrop-blur-sm p-3 rounded-lg shadow-lg border border-gray-100", children: [
|
|
25648
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-gray-800 mb-1", children: label }),
|
|
25649
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
25650
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600", children: [
|
|
25651
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Actual:" }),
|
|
25652
|
+
" ",
|
|
25653
|
+
Math.round(payload[0].value),
|
|
25654
|
+
" units"
|
|
25655
|
+
] }),
|
|
25656
|
+
payload[0].payload.idealOutput > 0 && /* @__PURE__ */ jsxs("p", { className: "text-xs text-gray-600", children: [
|
|
25657
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Target:" }),
|
|
25658
|
+
" ",
|
|
25659
|
+
Math.round(payload[0].payload.idealOutput),
|
|
25660
|
+
" units"
|
|
25661
|
+
] })
|
|
25662
|
+
] })
|
|
25663
|
+
] });
|
|
25664
|
+
}
|
|
25665
|
+
return null;
|
|
25666
|
+
};
|
|
26368
25667
|
var WorkspaceMonthlyHistory = ({
|
|
26369
25668
|
data,
|
|
26370
25669
|
month,
|
|
@@ -26416,8 +25715,31 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26416
25715
|
});
|
|
26417
25716
|
}
|
|
26418
25717
|
const avgIdealOutput = validDaysCount > 0 ? totalIdealOutput / validDaysCount : 0;
|
|
26419
|
-
|
|
25718
|
+
const calculatedMax = Math.max(maxOutput, avgIdealOutput);
|
|
25719
|
+
const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
|
|
25720
|
+
return { data: dailyData, maxOutput, avgIdealOutput, yAxisMax };
|
|
26420
25721
|
}, [data, month, year, selectedShift]);
|
|
25722
|
+
const yAxisTicks = useMemo(() => {
|
|
25723
|
+
const max = chartData.yAxisMax;
|
|
25724
|
+
const target = chartData.avgIdealOutput;
|
|
25725
|
+
if (!max || max <= 0) return void 0;
|
|
25726
|
+
const desiredIntervals = 4;
|
|
25727
|
+
const roughStep = max / desiredIntervals;
|
|
25728
|
+
const power = Math.pow(10, Math.floor(Math.log10(roughStep)));
|
|
25729
|
+
const normalized = roughStep / power;
|
|
25730
|
+
let step = 1 * power;
|
|
25731
|
+
if (normalized >= 1.5 && normalized < 3) step = 2 * power;
|
|
25732
|
+
else if (normalized >= 3 && normalized < 7) step = 5 * power;
|
|
25733
|
+
else if (normalized >= 7) step = 10 * power;
|
|
25734
|
+
const ticks = [];
|
|
25735
|
+
for (let v = 0; v <= max; v += step) {
|
|
25736
|
+
ticks.push(Math.round(v));
|
|
25737
|
+
}
|
|
25738
|
+
if (target > 0) {
|
|
25739
|
+
ticks.push(Math.round(target));
|
|
25740
|
+
}
|
|
25741
|
+
return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
|
|
25742
|
+
}, [chartData.yAxisMax, chartData.avgIdealOutput]);
|
|
26421
25743
|
const pieChartData = useMemo(() => {
|
|
26422
25744
|
const validShifts = data.map((d) => selectedShift === "day" ? d.dayShift : d.nightShift).filter(hasRealData);
|
|
26423
25745
|
if (validShifts.length === 0) return [];
|
|
@@ -26627,24 +25949,18 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26627
25949
|
}, children: [
|
|
26628
25950
|
metrics2?.avgEfficiency ?? 0,
|
|
26629
25951
|
"%"
|
|
26630
|
-
] }),
|
|
26631
|
-
/* @__PURE__ */ jsxs("div", { className: "text-[10px] text-gray-500 text-center mt-1", children: [
|
|
26632
|
-
new Date(year, month).toLocaleString("default", { month: "short" }),
|
|
26633
|
-
" avg"
|
|
26634
25952
|
] })
|
|
26635
25953
|
] }),
|
|
26636
25954
|
/* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4", children: [
|
|
26637
25955
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-gray-600 text-center mb-1", children: "Avg Daily Output" }),
|
|
26638
|
-
/* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-center text-gray-900", children: metrics2?.avgDailyOutput?.toLocaleString?.() ?? 0 })
|
|
26639
|
-
/* @__PURE__ */ jsx("div", { className: "text-[10px] text-gray-500 text-center mt-1", children: "units/day" })
|
|
25956
|
+
/* @__PURE__ */ jsx("div", { className: "text-2xl font-bold text-center text-gray-900", children: metrics2?.avgDailyOutput?.toLocaleString?.() ?? 0 })
|
|
26640
25957
|
] }),
|
|
26641
25958
|
/* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4", children: [
|
|
26642
25959
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-gray-600 text-center mb-1", children: "Avg Cycle Time" }),
|
|
26643
25960
|
/* @__PURE__ */ jsxs("div", { className: "text-2xl font-bold text-center text-gray-900", children: [
|
|
26644
25961
|
metrics2?.avgCycleTime ?? 0,
|
|
26645
25962
|
"s"
|
|
26646
|
-
] })
|
|
26647
|
-
/* @__PURE__ */ jsx("div", { className: "text-[10px] text-gray-500 text-center mt-1", children: "per unit" })
|
|
25963
|
+
] })
|
|
26648
25964
|
] })
|
|
26649
25965
|
] }),
|
|
26650
25966
|
/* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4", children: [
|
|
@@ -26708,43 +26024,55 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26708
26024
|
] }),
|
|
26709
26025
|
/* @__PURE__ */ jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-100 p-4 flex-1", children: [
|
|
26710
26026
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-700 mb-3", children: "Daily Output" }),
|
|
26711
|
-
/* @__PURE__ */ jsx("div", { style: { height: "
|
|
26027
|
+
/* @__PURE__ */ jsx("div", { style: { height: "220px" }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
|
|
26712
26028
|
BarChart$1,
|
|
26713
26029
|
{
|
|
26714
26030
|
data: chartData.data,
|
|
26715
|
-
margin: { top:
|
|
26031
|
+
margin: { top: 20, right: 10, bottom: 40, left: 10 },
|
|
26716
26032
|
children: [
|
|
26717
26033
|
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", vertical: false, stroke: "#f3f4f6" }),
|
|
26718
26034
|
/* @__PURE__ */ jsx(
|
|
26719
26035
|
XAxis,
|
|
26720
26036
|
{
|
|
26721
26037
|
dataKey: "hour",
|
|
26722
|
-
tick: { fontSize:
|
|
26038
|
+
tick: { fontSize: 10, fill: "#6b7280" },
|
|
26723
26039
|
interval: 0,
|
|
26724
26040
|
angle: -45,
|
|
26725
|
-
textAnchor: "end"
|
|
26041
|
+
textAnchor: "end",
|
|
26042
|
+
height: 60
|
|
26726
26043
|
}
|
|
26727
26044
|
),
|
|
26728
26045
|
/* @__PURE__ */ jsx(
|
|
26729
26046
|
YAxis,
|
|
26730
26047
|
{
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
|
|
26048
|
+
domain: [0, chartData.yAxisMax],
|
|
26049
|
+
width: 35,
|
|
26050
|
+
ticks: yAxisTicks,
|
|
26051
|
+
tick: (props) => {
|
|
26052
|
+
const { x, y, payload } = props;
|
|
26053
|
+
const value = Math.round(payload.value);
|
|
26054
|
+
const targetValue = Math.round(chartData.avgIdealOutput);
|
|
26055
|
+
const isTarget = Math.abs(value - targetValue) < 1 && targetValue > 0;
|
|
26056
|
+
return /* @__PURE__ */ jsx(
|
|
26057
|
+
"text",
|
|
26058
|
+
{
|
|
26059
|
+
x: x - 5,
|
|
26060
|
+
y: y + 4,
|
|
26061
|
+
textAnchor: "end",
|
|
26062
|
+
fontSize: "10",
|
|
26063
|
+
fill: isTarget ? "#E34329" : "#6b7280",
|
|
26064
|
+
fontWeight: isTarget ? "600" : "normal",
|
|
26065
|
+
children: value < 1e-3 ? "" : value.toString()
|
|
26066
|
+
}
|
|
26067
|
+
);
|
|
26068
|
+
}
|
|
26734
26069
|
}
|
|
26735
26070
|
),
|
|
26736
26071
|
/* @__PURE__ */ jsx(
|
|
26737
26072
|
Tooltip,
|
|
26738
26073
|
{
|
|
26739
26074
|
cursor: false,
|
|
26740
|
-
|
|
26741
|
-
backgroundColor: "rgba(255, 255, 255, 0.95)",
|
|
26742
|
-
border: "1px solid #e5e7eb",
|
|
26743
|
-
borderRadius: "6px",
|
|
26744
|
-
fontSize: "11px",
|
|
26745
|
-
padding: "8px"
|
|
26746
|
-
},
|
|
26747
|
-
labelStyle: { fontSize: "11px", fontWeight: "600" }
|
|
26075
|
+
content: CustomTooltip
|
|
26748
26076
|
}
|
|
26749
26077
|
),
|
|
26750
26078
|
chartData.avgIdealOutput > 0 && /* @__PURE__ */ jsx(
|
|
@@ -26752,40 +26080,68 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26752
26080
|
{
|
|
26753
26081
|
y: chartData.avgIdealOutput,
|
|
26754
26082
|
stroke: "#E34329",
|
|
26755
|
-
strokeDasharray: "
|
|
26083
|
+
strokeDasharray: "5 5",
|
|
26756
26084
|
strokeWidth: 2
|
|
26757
26085
|
}
|
|
26758
26086
|
),
|
|
26759
|
-
/* @__PURE__ */
|
|
26760
|
-
|
|
26761
|
-
|
|
26762
|
-
|
|
26763
|
-
|
|
26764
|
-
|
|
26765
|
-
|
|
26766
|
-
|
|
26767
|
-
|
|
26768
|
-
|
|
26769
|
-
|
|
26770
|
-
|
|
26771
|
-
|
|
26087
|
+
/* @__PURE__ */ jsxs(Bar, { dataKey: "output", radius: [4, 4, 0, 0], children: [
|
|
26088
|
+
chartData.data.map((entry, index) => /* @__PURE__ */ jsx(
|
|
26089
|
+
Cell,
|
|
26090
|
+
{
|
|
26091
|
+
fill: entry.output === 0 ? "#f3f4f6" : entry.color,
|
|
26092
|
+
style: {
|
|
26093
|
+
filter: "brightness(1)",
|
|
26094
|
+
transition: "all 0.3s ease",
|
|
26095
|
+
cursor: entry.output > 0 ? "pointer" : "default"
|
|
26096
|
+
},
|
|
26097
|
+
onMouseEnter: (e) => {
|
|
26098
|
+
if (entry.output > 0) {
|
|
26099
|
+
const target = e.target;
|
|
26100
|
+
target.style.filter = "brightness(1.1)";
|
|
26101
|
+
target.style.transform = "translateY(-2px)";
|
|
26102
|
+
}
|
|
26103
|
+
},
|
|
26104
|
+
onMouseLeave: (e) => {
|
|
26105
|
+
const target = e.target;
|
|
26106
|
+
target.style.filter = "brightness(1)";
|
|
26107
|
+
target.style.transform = "translateY(0)";
|
|
26108
|
+
}
|
|
26772
26109
|
},
|
|
26773
|
-
|
|
26774
|
-
|
|
26775
|
-
|
|
26776
|
-
|
|
26110
|
+
`cell-${index}`
|
|
26111
|
+
)),
|
|
26112
|
+
/* @__PURE__ */ jsx(
|
|
26113
|
+
LabelList,
|
|
26114
|
+
{
|
|
26115
|
+
dataKey: "originalOutput",
|
|
26116
|
+
position: "top",
|
|
26117
|
+
content: (props) => {
|
|
26118
|
+
const { x, y, width, value } = props;
|
|
26119
|
+
if (!value || value === 0) return null;
|
|
26120
|
+
return /* @__PURE__ */ jsx(
|
|
26121
|
+
"text",
|
|
26122
|
+
{
|
|
26123
|
+
x: x + width / 2,
|
|
26124
|
+
y: y - 5,
|
|
26125
|
+
textAnchor: "middle",
|
|
26126
|
+
fontSize: "11",
|
|
26127
|
+
fontWeight: "600",
|
|
26128
|
+
fill: "#374151",
|
|
26129
|
+
children: Math.round(value)
|
|
26130
|
+
}
|
|
26131
|
+
);
|
|
26132
|
+
}
|
|
26777
26133
|
}
|
|
26778
|
-
|
|
26779
|
-
|
|
26780
|
-
)) })
|
|
26134
|
+
)
|
|
26135
|
+
] })
|
|
26781
26136
|
]
|
|
26782
26137
|
}
|
|
26783
26138
|
) }) }),
|
|
26784
|
-
|
|
26785
|
-
/* @__PURE__ */ jsx("div", { className: "w-
|
|
26786
|
-
/* @__PURE__ */ jsxs("span", { className: "text-
|
|
26139
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center items-center gap-6 mt-3", children: chartData.avgIdealOutput > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
26140
|
+
/* @__PURE__ */ jsx("div", { className: "w-12 h-0.5 border-t-2 border-dashed", style: { borderColor: "#E34329" } }),
|
|
26141
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-600", children: [
|
|
26787
26142
|
"Target: ",
|
|
26788
|
-
Math.round(chartData.avgIdealOutput)
|
|
26143
|
+
Math.round(chartData.avgIdealOutput),
|
|
26144
|
+
" units/day"
|
|
26789
26145
|
] })
|
|
26790
26146
|
] }) })
|
|
26791
26147
|
] })
|
|
@@ -27298,7 +26654,7 @@ function Skeleton({ className, ...props }) {
|
|
|
27298
26654
|
var Select = SelectPrimitive.Root;
|
|
27299
26655
|
var SelectGroup = SelectPrimitive.Group;
|
|
27300
26656
|
var SelectValue = SelectPrimitive.Value;
|
|
27301
|
-
var SelectTrigger =
|
|
26657
|
+
var SelectTrigger = React20.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
27302
26658
|
SelectPrimitive.Trigger,
|
|
27303
26659
|
{
|
|
27304
26660
|
ref,
|
|
@@ -27314,7 +26670,7 @@ var SelectTrigger = React19.forwardRef(({ className, children, ...props }, ref)
|
|
|
27314
26670
|
}
|
|
27315
26671
|
));
|
|
27316
26672
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
27317
|
-
var SelectScrollUpButton =
|
|
26673
|
+
var SelectScrollUpButton = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
27318
26674
|
SelectPrimitive.ScrollUpButton,
|
|
27319
26675
|
{
|
|
27320
26676
|
ref,
|
|
@@ -27324,7 +26680,7 @@ var SelectScrollUpButton = React19.forwardRef(({ className, ...props }, ref) =>
|
|
|
27324
26680
|
}
|
|
27325
26681
|
));
|
|
27326
26682
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
27327
|
-
var SelectScrollDownButton =
|
|
26683
|
+
var SelectScrollDownButton = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
27328
26684
|
SelectPrimitive.ScrollDownButton,
|
|
27329
26685
|
{
|
|
27330
26686
|
ref,
|
|
@@ -27334,7 +26690,7 @@ var SelectScrollDownButton = React19.forwardRef(({ className, ...props }, ref) =
|
|
|
27334
26690
|
}
|
|
27335
26691
|
));
|
|
27336
26692
|
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
27337
|
-
var SelectContent =
|
|
26693
|
+
var SelectContent = React20.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
27338
26694
|
SelectPrimitive.Content,
|
|
27339
26695
|
{
|
|
27340
26696
|
ref,
|
|
@@ -27362,7 +26718,7 @@ var SelectContent = React19.forwardRef(({ className, children, position = "poppe
|
|
|
27362
26718
|
}
|
|
27363
26719
|
) }));
|
|
27364
26720
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
27365
|
-
var SelectLabel =
|
|
26721
|
+
var SelectLabel = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
27366
26722
|
SelectPrimitive.Label,
|
|
27367
26723
|
{
|
|
27368
26724
|
ref,
|
|
@@ -27371,7 +26727,7 @@ var SelectLabel = React19.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
27371
26727
|
}
|
|
27372
26728
|
));
|
|
27373
26729
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
27374
|
-
var SelectItem =
|
|
26730
|
+
var SelectItem = React20.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
27375
26731
|
SelectPrimitive.Item,
|
|
27376
26732
|
{
|
|
27377
26733
|
ref,
|
|
@@ -27387,7 +26743,7 @@ var SelectItem = React19.forwardRef(({ className, children, ...props }, ref) =>
|
|
|
27387
26743
|
}
|
|
27388
26744
|
));
|
|
27389
26745
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
27390
|
-
var SelectSeparator =
|
|
26746
|
+
var SelectSeparator = React20.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
27391
26747
|
SelectPrimitive.Separator,
|
|
27392
26748
|
{
|
|
27393
26749
|
ref,
|
|
@@ -27680,7 +27036,7 @@ if (typeof document !== "undefined") {
|
|
|
27680
27036
|
document.head.appendChild(style);
|
|
27681
27037
|
}
|
|
27682
27038
|
}
|
|
27683
|
-
var VideoPlayer =
|
|
27039
|
+
var VideoPlayer = React20__default.forwardRef(({
|
|
27684
27040
|
src,
|
|
27685
27041
|
poster,
|
|
27686
27042
|
autoplay = false,
|
|
@@ -27965,7 +27321,7 @@ var VideoPlayer = React19__default.forwardRef(({
|
|
|
27965
27321
|
setIsReady(false);
|
|
27966
27322
|
}
|
|
27967
27323
|
}, []);
|
|
27968
|
-
|
|
27324
|
+
React20__default.useImperativeHandle(ref, () => ({
|
|
27969
27325
|
player: playerRef.current,
|
|
27970
27326
|
play,
|
|
27971
27327
|
pause,
|
|
@@ -28581,7 +27937,7 @@ var NewClipsNotification = ({
|
|
|
28581
27937
|
}
|
|
28582
27938
|
);
|
|
28583
27939
|
};
|
|
28584
|
-
var
|
|
27940
|
+
var getSupabaseClient2 = () => {
|
|
28585
27941
|
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
28586
27942
|
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
28587
27943
|
if (!url || !key) {
|
|
@@ -28589,9 +27945,9 @@ var getSupabaseClient3 = () => {
|
|
|
28589
27945
|
}
|
|
28590
27946
|
return createClient(url, key);
|
|
28591
27947
|
};
|
|
28592
|
-
var
|
|
27948
|
+
var getAuthToken2 = async () => {
|
|
28593
27949
|
try {
|
|
28594
|
-
const supabase =
|
|
27950
|
+
const supabase = getSupabaseClient2();
|
|
28595
27951
|
const { data: { session } } = await supabase.auth.getSession();
|
|
28596
27952
|
return session?.access_token || null;
|
|
28597
27953
|
} catch (error) {
|
|
@@ -28612,7 +27968,7 @@ function useWorkspaceCrop(workspaceId) {
|
|
|
28612
27968
|
setIsLoading(true);
|
|
28613
27969
|
setError(null);
|
|
28614
27970
|
try {
|
|
28615
|
-
const token = await
|
|
27971
|
+
const token = await getAuthToken2();
|
|
28616
27972
|
if (!token) {
|
|
28617
27973
|
throw new Error("Authentication required");
|
|
28618
27974
|
}
|
|
@@ -28645,6 +28001,47 @@ function useWorkspaceCrop(workspaceId) {
|
|
|
28645
28001
|
}, [workspaceId]);
|
|
28646
28002
|
return { crop, isLoading, error };
|
|
28647
28003
|
}
|
|
28004
|
+
var FilterDialogTrigger = ({
|
|
28005
|
+
className = ""
|
|
28006
|
+
}) => {
|
|
28007
|
+
const { state, toggleAdvancedPanel } = useClipFilter();
|
|
28008
|
+
const activeFiltersCount = [
|
|
28009
|
+
state.showFastCycles,
|
|
28010
|
+
state.showSlowCycles,
|
|
28011
|
+
state.showLongestIdles,
|
|
28012
|
+
state.showCycleCompletion,
|
|
28013
|
+
state.showIdleTime
|
|
28014
|
+
].filter(Boolean).length;
|
|
28015
|
+
const isDefaultPercentile = state.percentile === 10;
|
|
28016
|
+
const isDefaultShift = state.shiftFilter === void 0;
|
|
28017
|
+
const hasCustomFilters = !isDefaultPercentile || !isDefaultShift || activeFiltersCount < 5;
|
|
28018
|
+
return /* @__PURE__ */ jsxs(
|
|
28019
|
+
"button",
|
|
28020
|
+
{
|
|
28021
|
+
onClick: toggleAdvancedPanel,
|
|
28022
|
+
className: `relative group p-2.5 bg-gradient-to-r from-blue-50 to-indigo-50 hover:from-blue-100 hover:to-indigo-100 border border-blue-200/60 hover:border-blue-300 rounded-xl transition-all duration-200 hover:shadow-lg hover:shadow-blue-100/50 ${className}`,
|
|
28023
|
+
title: "Advanced Filters",
|
|
28024
|
+
children: [
|
|
28025
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
28026
|
+
/* @__PURE__ */ jsx(Sliders, { className: "h-5 w-5 text-blue-600 group-hover:text-blue-700 transition-colors" }),
|
|
28027
|
+
hasCustomFilters && /* @__PURE__ */ jsx(Sparkles, { className: "absolute -top-1 -right-1 h-3 w-3 text-yellow-500 animate-pulse" })
|
|
28028
|
+
] }),
|
|
28029
|
+
hasCustomFilters && /* @__PURE__ */ jsx("div", { className: "absolute -top-1 -right-1 bg-gradient-to-r from-yellow-400 to-orange-400 text-white text-xs font-bold rounded-full h-5 w-5 flex items-center justify-center shadow-lg animate-bounce", children: activeFiltersCount < 5 ? activeFiltersCount : "!" }),
|
|
28030
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute bottom-full right-0 mb-2 px-3 py-1.5 bg-gray-900 text-white text-xs rounded-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none whitespace-nowrap", children: [
|
|
28031
|
+
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
28032
|
+
/* @__PURE__ */ jsx("div", { className: "font-semibold", children: "Advanced Filters" }),
|
|
28033
|
+
/* @__PURE__ */ jsx("div", { className: "text-gray-300", children: hasCustomFilters ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
28034
|
+
!isDefaultPercentile && `${state.percentile}% percentile`,
|
|
28035
|
+
!isDefaultShift && ` \u2022 Shift ${state.shiftFilter === 0 ? "Day" : "Night"}`,
|
|
28036
|
+
activeFiltersCount < 5 && ` \u2022 ${activeFiltersCount}/5 types`
|
|
28037
|
+
] }) : "Default settings" })
|
|
28038
|
+
] }),
|
|
28039
|
+
/* @__PURE__ */ jsx("div", { className: "absolute top-full right-4 w-0 h-0 border-l-2 border-r-2 border-t-4 border-transparent border-t-gray-900" })
|
|
28040
|
+
] })
|
|
28041
|
+
]
|
|
28042
|
+
}
|
|
28043
|
+
);
|
|
28044
|
+
};
|
|
28648
28045
|
var getSeverityIcon = (severity) => {
|
|
28649
28046
|
switch (severity) {
|
|
28650
28047
|
case "high":
|
|
@@ -28708,6 +28105,10 @@ var FileManagerFilters = ({
|
|
|
28708
28105
|
const [loadingCategories, setLoadingCategories] = useState(/* @__PURE__ */ new Set());
|
|
28709
28106
|
const [categoryPages, setCategoryPages] = useState({});
|
|
28710
28107
|
const [categoryHasMore, setCategoryHasMore] = useState({});
|
|
28108
|
+
const { state: filterState, updatePercentile } = useClipFilter();
|
|
28109
|
+
const [percentileCounts, setPercentileCounts] = useState({});
|
|
28110
|
+
const [percentileClips, setPercentileClips] = useState({});
|
|
28111
|
+
const [loadingPercentile, setLoadingPercentile] = useState(false);
|
|
28711
28112
|
const fetchClipMetadata = useCallback(async (categoryId, page = 1) => {
|
|
28712
28113
|
if (!workspaceId || !date || shift === void 0) {
|
|
28713
28114
|
console.warn("[FileManager] Missing required params for clip metadata fetch");
|
|
@@ -28720,7 +28121,7 @@ var FileManagerFilters = ({
|
|
|
28720
28121
|
method: "POST",
|
|
28721
28122
|
headers: {
|
|
28722
28123
|
"Content-Type": "application/json",
|
|
28723
|
-
"Authorization": `Bearer ${await
|
|
28124
|
+
"Authorization": `Bearer ${await getAuthToken3()}`
|
|
28724
28125
|
},
|
|
28725
28126
|
body: JSON.stringify({
|
|
28726
28127
|
action: "clip-metadata",
|
|
@@ -28753,10 +28154,10 @@ var FileManagerFilters = ({
|
|
|
28753
28154
|
});
|
|
28754
28155
|
}
|
|
28755
28156
|
}, [workspaceId, date, shift]);
|
|
28756
|
-
const
|
|
28157
|
+
const getAuthToken3 = async () => {
|
|
28757
28158
|
try {
|
|
28758
|
-
const { createClient:
|
|
28759
|
-
const supabase =
|
|
28159
|
+
const { createClient: createClient4 } = await import('@supabase/supabase-js');
|
|
28160
|
+
const supabase = createClient4(
|
|
28760
28161
|
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
28761
28162
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
|
|
28762
28163
|
);
|
|
@@ -28767,6 +28168,91 @@ var FileManagerFilters = ({
|
|
|
28767
28168
|
return null;
|
|
28768
28169
|
}
|
|
28769
28170
|
};
|
|
28171
|
+
const fetchPercentileClips = useCallback(async (type) => {
|
|
28172
|
+
if (!workspaceId || !date || shift === void 0) {
|
|
28173
|
+
console.warn("[FileManager] Missing required params for percentile clips fetch");
|
|
28174
|
+
return;
|
|
28175
|
+
}
|
|
28176
|
+
try {
|
|
28177
|
+
const startDate = `${date}T00:00:00Z`;
|
|
28178
|
+
const endDate = `${date}T23:59:59Z`;
|
|
28179
|
+
const response = await fetch("/api/clips/supabase", {
|
|
28180
|
+
method: "POST",
|
|
28181
|
+
headers: {
|
|
28182
|
+
"Content-Type": "application/json",
|
|
28183
|
+
"Authorization": `Bearer ${await getAuthToken3()}`
|
|
28184
|
+
},
|
|
28185
|
+
body: JSON.stringify({
|
|
28186
|
+
action: "percentile-clips",
|
|
28187
|
+
workspaceId,
|
|
28188
|
+
startDate,
|
|
28189
|
+
endDate,
|
|
28190
|
+
percentile: filterState.percentile,
|
|
28191
|
+
shiftId: shift,
|
|
28192
|
+
limit: 50,
|
|
28193
|
+
// The actual percentile action (fast-cycles, slow-cycles, idle-times)
|
|
28194
|
+
percentileAction: type
|
|
28195
|
+
})
|
|
28196
|
+
});
|
|
28197
|
+
if (!response.ok) {
|
|
28198
|
+
throw new Error(`API error: ${response.status}`);
|
|
28199
|
+
}
|
|
28200
|
+
const data = await response.json();
|
|
28201
|
+
setPercentileClips((prev) => ({
|
|
28202
|
+
...prev,
|
|
28203
|
+
[type]: data.clips || []
|
|
28204
|
+
}));
|
|
28205
|
+
setPercentileCounts((prev) => ({
|
|
28206
|
+
...prev,
|
|
28207
|
+
[type]: data.total || 0
|
|
28208
|
+
}));
|
|
28209
|
+
console.log(`[FileManager] Loaded ${data.clips?.length || 0} ${type} clips (percentile: ${filterState.percentile}%)`);
|
|
28210
|
+
} catch (error) {
|
|
28211
|
+
console.error(`[FileManager] Error fetching ${type} clips:`, error);
|
|
28212
|
+
}
|
|
28213
|
+
}, [workspaceId, date, shift, filterState.percentile]);
|
|
28214
|
+
useEffect(() => {
|
|
28215
|
+
if (workspaceId && date && shift !== void 0) {
|
|
28216
|
+
setLoadingPercentile(true);
|
|
28217
|
+
Promise.all([
|
|
28218
|
+
fetchPercentileClips("fast-cycles"),
|
|
28219
|
+
fetchPercentileClips("slow-cycles")
|
|
28220
|
+
// fetchPercentileClips('idle-times') // Temporarily commented out
|
|
28221
|
+
]).finally(() => {
|
|
28222
|
+
setLoadingPercentile(false);
|
|
28223
|
+
});
|
|
28224
|
+
}
|
|
28225
|
+
}, [fetchPercentileClips]);
|
|
28226
|
+
const shouldShowCategory = useCallback((categoryId) => {
|
|
28227
|
+
switch (categoryId) {
|
|
28228
|
+
case "fast-cycles":
|
|
28229
|
+
return filterState.showFastCycles;
|
|
28230
|
+
case "slow-cycles":
|
|
28231
|
+
return filterState.showSlowCycles;
|
|
28232
|
+
case "longest-idles":
|
|
28233
|
+
return false;
|
|
28234
|
+
// filterState.showLongestIdles; // Temporarily disabled
|
|
28235
|
+
case "cycle_completion":
|
|
28236
|
+
return filterState.showCycleCompletion;
|
|
28237
|
+
case "idle_time":
|
|
28238
|
+
return filterState.showIdleTime;
|
|
28239
|
+
case "sop_deviations":
|
|
28240
|
+
return filterState.showSopDeviations;
|
|
28241
|
+
default:
|
|
28242
|
+
return true;
|
|
28243
|
+
}
|
|
28244
|
+
}, [filterState]);
|
|
28245
|
+
const getPercentileIcon = useCallback((type, isExpanded, colorClasses) => {
|
|
28246
|
+
const iconMap = {
|
|
28247
|
+
"fast-cycles": { icon: TrendingUp, color: "text-green-600" },
|
|
28248
|
+
"slow-cycles": { icon: TrendingDown, color: "text-red-600" },
|
|
28249
|
+
"longest-idles": { icon: Clock, color: "text-orange-600" }
|
|
28250
|
+
};
|
|
28251
|
+
const config = iconMap[type];
|
|
28252
|
+
if (!config) return null;
|
|
28253
|
+
const IconComponent = config.icon;
|
|
28254
|
+
return isExpanded ? /* @__PURE__ */ jsx(FolderOpen, { className: `h-4 w-4 ${config.color}` }) : /* @__PURE__ */ jsx(IconComponent, { className: `h-4 w-4 ${config.color}` });
|
|
28255
|
+
}, []);
|
|
28770
28256
|
const filterTree = useMemo(() => {
|
|
28771
28257
|
const tree = [];
|
|
28772
28258
|
categories.forEach((category) => {
|
|
@@ -28778,7 +28264,7 @@ var FileManagerFilters = ({
|
|
|
28778
28264
|
(clip) => clip.description.toLowerCase().includes(searchTerm.toLowerCase()) || clip.clip_timestamp.includes(searchTerm) || clip.severity.toLowerCase().includes(searchTerm.toLowerCase())
|
|
28779
28265
|
);
|
|
28780
28266
|
}
|
|
28781
|
-
if (categoryCount > 0) {
|
|
28267
|
+
if (categoryCount > 0 && shouldShowCategory(category.id)) {
|
|
28782
28268
|
const colorClasses = getColorClasses(category.color);
|
|
28783
28269
|
const clipNodes = filteredClips.map((clip) => {
|
|
28784
28270
|
const timeString = new Date(clip.clip_timestamp).toLocaleTimeString("en-US", {
|
|
@@ -28802,6 +28288,8 @@ var FileManagerFilters = ({
|
|
|
28802
28288
|
tree.push({
|
|
28803
28289
|
id: category.id,
|
|
28804
28290
|
label: category.label,
|
|
28291
|
+
subtitle: category.subtitle || category.description,
|
|
28292
|
+
description: category.description,
|
|
28805
28293
|
type: "category",
|
|
28806
28294
|
count: categoryCount,
|
|
28807
28295
|
// Use API count
|
|
@@ -28812,8 +28300,84 @@ var FileManagerFilters = ({
|
|
|
28812
28300
|
});
|
|
28813
28301
|
}
|
|
28814
28302
|
});
|
|
28303
|
+
const percentileCategories = [
|
|
28304
|
+
{
|
|
28305
|
+
id: "fast-cycles",
|
|
28306
|
+
label: "Fast Cycles",
|
|
28307
|
+
subtitle: `Top ${filterState.percentile}% fastest performance`,
|
|
28308
|
+
description: `Top ${filterState.percentile}% fastest performance`,
|
|
28309
|
+
type: "percentile-category",
|
|
28310
|
+
count: percentileCounts["fast-cycles"] || 0,
|
|
28311
|
+
icon: getPercentileIcon("fast-cycles", expandedNodes.has("fast-cycles"), { text: "text-green-600" }),
|
|
28312
|
+
color: "green",
|
|
28313
|
+
percentileType: "fast-cycles",
|
|
28314
|
+
isPercentile: true,
|
|
28315
|
+
children: (percentileClips["fast-cycles"] || []).map((clip, index) => ({
|
|
28316
|
+
id: clip.id,
|
|
28317
|
+
// Remove prefix to match currentVideoId
|
|
28318
|
+
label: `${clip.timestamp} - ${clip.description}${clip.cycle_time_seconds ? ` (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
|
|
28319
|
+
type: "video",
|
|
28320
|
+
icon: getSeverityIcon(clip.severity),
|
|
28321
|
+
timestamp: clip.creation_timestamp,
|
|
28322
|
+
severity: clip.severity,
|
|
28323
|
+
clipId: clip.id,
|
|
28324
|
+
categoryId: "fast-cycles"
|
|
28325
|
+
}))
|
|
28326
|
+
},
|
|
28327
|
+
{
|
|
28328
|
+
id: "slow-cycles",
|
|
28329
|
+
label: "Slow Cycles",
|
|
28330
|
+
subtitle: `Top ${filterState.percentile}% slowest performance`,
|
|
28331
|
+
description: `Top ${filterState.percentile}% slowest performance`,
|
|
28332
|
+
type: "percentile-category",
|
|
28333
|
+
count: percentileCounts["slow-cycles"] || 0,
|
|
28334
|
+
icon: getPercentileIcon("slow-cycles", expandedNodes.has("slow-cycles"), { text: "text-red-600" }),
|
|
28335
|
+
color: "red",
|
|
28336
|
+
percentileType: "slow-cycles",
|
|
28337
|
+
isPercentile: true,
|
|
28338
|
+
children: (percentileClips["slow-cycles"] || []).map((clip, index) => ({
|
|
28339
|
+
id: clip.id,
|
|
28340
|
+
// Remove prefix to match currentVideoId
|
|
28341
|
+
label: `${clip.timestamp} - ${clip.description}${clip.cycle_time_seconds ? ` (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
|
|
28342
|
+
type: "video",
|
|
28343
|
+
icon: getSeverityIcon(clip.severity),
|
|
28344
|
+
timestamp: clip.creation_timestamp,
|
|
28345
|
+
severity: clip.severity,
|
|
28346
|
+
clipId: clip.id,
|
|
28347
|
+
categoryId: "slow-cycles"
|
|
28348
|
+
}))
|
|
28349
|
+
}
|
|
28350
|
+
// Temporarily commented out longest idle times
|
|
28351
|
+
/* {
|
|
28352
|
+
id: 'longest-idles',
|
|
28353
|
+
label: 'Longest Idle Times',
|
|
28354
|
+
subtitle: `Top ${filterState.percentile}% longest idle periods`,
|
|
28355
|
+
description: `Top ${filterState.percentile}% longest idle periods`,
|
|
28356
|
+
type: 'percentile-category' as const,
|
|
28357
|
+
count: percentileCounts['idle-times'] || 0,
|
|
28358
|
+
icon: getPercentileIcon('longest-idles', expandedNodes.has('longest-idles'), { text: 'text-orange-600' }),
|
|
28359
|
+
color: 'orange',
|
|
28360
|
+
percentileType: 'idle-times' as const,
|
|
28361
|
+
isPercentile: true,
|
|
28362
|
+
children: (percentileClips['idle-times'] || []).map((clip, index) => ({
|
|
28363
|
+
id: clip.id, // Remove prefix to match currentVideoId
|
|
28364
|
+
label: `${clip.timestamp} - ${clip.description}${clip.cycle_time_seconds ? ` (${clip.cycle_time_seconds.toFixed(1)}s)` : ''}`,
|
|
28365
|
+
type: 'video' as const,
|
|
28366
|
+
icon: getSeverityIcon(clip.severity),
|
|
28367
|
+
timestamp: clip.creation_timestamp,
|
|
28368
|
+
severity: clip.severity,
|
|
28369
|
+
clipId: clip.id,
|
|
28370
|
+
categoryId: 'longest-idles'
|
|
28371
|
+
}))
|
|
28372
|
+
} */
|
|
28373
|
+
];
|
|
28374
|
+
percentileCategories.forEach((category) => {
|
|
28375
|
+
if (category.count > 0 && shouldShowCategory(category.id)) {
|
|
28376
|
+
tree.unshift(category);
|
|
28377
|
+
}
|
|
28378
|
+
});
|
|
28815
28379
|
return tree;
|
|
28816
|
-
}, [categories, expandedNodes, searchTerm, counts, clipMetadata]);
|
|
28380
|
+
}, [categories, expandedNodes, searchTerm, counts, clipMetadata, filterState.percentile, percentileCounts, percentileClips, shouldShowCategory, getPercentileIcon]);
|
|
28817
28381
|
const toggleExpanded = (nodeId) => {
|
|
28818
28382
|
const newExpanded = new Set(expandedNodes);
|
|
28819
28383
|
if (newExpanded.has(nodeId)) {
|
|
@@ -28829,7 +28393,7 @@ var FileManagerFilters = ({
|
|
|
28829
28393
|
setExpandedNodes(newExpanded);
|
|
28830
28394
|
};
|
|
28831
28395
|
const handleNodeClick = (node) => {
|
|
28832
|
-
if (node.type === "category") {
|
|
28396
|
+
if (node.type === "category" || node.type === "percentile-category") {
|
|
28833
28397
|
toggleExpanded(node.id);
|
|
28834
28398
|
onFilterChange(node.id);
|
|
28835
28399
|
} else if (node.type === "video") {
|
|
@@ -28848,7 +28412,7 @@ var FileManagerFilters = ({
|
|
|
28848
28412
|
const isExpanded = expandedNodes.has(node.id);
|
|
28849
28413
|
const isActive = activeFilter === node.id;
|
|
28850
28414
|
const isCurrentVideo = currentVideoId === node.id;
|
|
28851
|
-
const hasChildren = node.
|
|
28415
|
+
const hasChildren = (node.count || 0) > 0;
|
|
28852
28416
|
const colorClasses = node.color ? getColorClasses(node.color) : null;
|
|
28853
28417
|
return /* @__PURE__ */ jsxs("div", { className: "select-none animate-in", children: [
|
|
28854
28418
|
/* @__PURE__ */ jsxs(
|
|
@@ -28865,25 +28429,26 @@ var FileManagerFilters = ({
|
|
|
28865
28429
|
e.stopPropagation();
|
|
28866
28430
|
toggleExpanded(node.id);
|
|
28867
28431
|
},
|
|
28868
|
-
children: isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: `text-slate-600 group-hover:text-blue-600 transition-colors duration-200 ${node.type === "category" ? "h-4 w-4" : "h-3.5 w-3.5"}` }) : /* @__PURE__ */ jsx(ChevronRight, { className: `text-slate-600 group-hover:text-blue-600 transition-colors duration-200 ${node.type === "category" ? "h-4 w-4" : "h-3.5 w-3.5"}` })
|
|
28432
|
+
children: isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: `text-slate-600 group-hover:text-blue-600 transition-colors duration-200 ${node.type === "category" || node.type === "percentile-category" ? "h-4 w-4" : "h-3.5 w-3.5"}` }) : /* @__PURE__ */ jsx(ChevronRight, { className: `text-slate-600 group-hover:text-blue-600 transition-colors duration-200 ${node.type === "category" || node.type === "percentile-category" ? "h-4 w-4" : "h-3.5 w-3.5"}` })
|
|
28869
28433
|
}
|
|
28870
28434
|
),
|
|
28871
|
-
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 mr-3 ${node.type === "category" ? "p-2 rounded-lg shadow-sm group-hover:scale-110 transition-transform duration-200" : "p-0.5"} ${colorClasses && node.type === "category" ? `${colorClasses.bg} border border-white/60` : ""}`, children: node.icon }),
|
|
28435
|
+
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 mr-3 ${node.type === "category" || node.type === "percentile-category" ? "p-2 rounded-lg shadow-sm group-hover:scale-110 transition-transform duration-200" : "p-0.5"} ${colorClasses && (node.type === "category" || node.type === "percentile-category") ? `${colorClasses.bg} border border-white/60` : ""}`, children: node.icon }),
|
|
28872
28436
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between", children: [
|
|
28873
28437
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
28874
|
-
/* @__PURE__ */ jsx("div", { className: `font-semibold tracking-tight ${node.type === "category" ? "text-slate-800 text-sm" : "text-slate-700 text-xs"} ${isCurrentVideo ? "text-emerald-700 font-bold" : ""} group-hover:text-slate-900 transition-colors duration-200`, children: node.label }),
|
|
28438
|
+
/* @__PURE__ */ jsx("div", { className: `font-semibold tracking-tight ${node.type === "category" || node.type === "percentile-category" ? "text-slate-800 text-sm" : "text-slate-700 text-xs"} ${isCurrentVideo ? "text-emerald-700 font-bold" : ""} group-hover:text-slate-900 transition-colors duration-200`, children: node.label }),
|
|
28875
28439
|
node.type === "category" && categories.find((c) => c.id === node.id)?.description && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: categories.find((c) => c.id === node.id)?.description }),
|
|
28440
|
+
node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
|
|
28876
28441
|
node.type === "video" && node.severity && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${node.severity === "high" ? "bg-red-100 text-red-700" : node.severity === "medium" ? "bg-yellow-100 text-yellow-700" : "bg-green-100 text-green-700"}`, children: [
|
|
28877
28442
|
node.severity,
|
|
28878
28443
|
" priority"
|
|
28879
28444
|
] }) })
|
|
28880
28445
|
] }),
|
|
28881
|
-
node.count !== void 0 && node.type === "category" && /* @__PURE__ */ jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsx("span", { className: `px-2.5 py-1 text-sm font-bold rounded-lg shadow-sm border backdrop-blur-sm flex-shrink-0 group-hover:scale-105 transition-all duration-200 ${colorClasses ? `${colorClasses.bg} ${colorClasses.text} ${colorClasses.border} bg-opacity-80` : "bg-slate-100/80 text-slate-700 border-slate-200/60"}`, children: node.count }) })
|
|
28446
|
+
node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsx("span", { className: `px-2.5 py-1 text-sm font-bold rounded-lg shadow-sm border backdrop-blur-sm flex-shrink-0 group-hover:scale-105 transition-all duration-200 ${colorClasses ? `${colorClasses.bg} ${colorClasses.text} ${colorClasses.border} bg-opacity-80` : "bg-slate-100/80 text-slate-700 border-slate-200/60"}`, children: node.count }) })
|
|
28882
28447
|
] })
|
|
28883
28448
|
]
|
|
28884
28449
|
}
|
|
28885
28450
|
),
|
|
28886
|
-
hasChildren && isExpanded && node.type === "category" && /* @__PURE__ */ jsx("div", { className: "mt-2 ml-3 animate-in border-l-2 border-slate-100 pl-3", children: /* @__PURE__ */ jsxs("div", { className: "max-h-64 overflow-y-auto space-y-1 scrollbar-thin scrollbar-track-slate-100 scrollbar-thumb-slate-300", children: [
|
|
28451
|
+
hasChildren && isExpanded && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsx("div", { className: "mt-2 ml-3 animate-in border-l-2 border-slate-100 pl-3", children: /* @__PURE__ */ jsxs("div", { className: "max-h-64 overflow-y-auto space-y-1 scrollbar-thin scrollbar-track-slate-100 scrollbar-thumb-slate-300", children: [
|
|
28887
28452
|
node.children.map((child) => renderNode(child, depth + 1)),
|
|
28888
28453
|
loadingCategories.has(`${node.id}-${(categoryPages[node.id] || 0) + 1}`) && /* @__PURE__ */ jsx("div", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center text-sm text-slate-500", children: [
|
|
28889
28454
|
/* @__PURE__ */ jsx("div", { className: "animate-spin mr-2 h-4 w-4 border-2 border-slate-300 border-t-blue-500 rounded-full" }),
|
|
@@ -28909,11 +28474,14 @@ var FileManagerFilters = ({
|
|
|
28909
28474
|
] }, node.id);
|
|
28910
28475
|
};
|
|
28911
28476
|
return /* @__PURE__ */ jsxs("div", { className: `bg-white rounded-2xl shadow-lg border border-gray-100 h-full hover:shadow-xl transition-all duration-300 ease-out backdrop-blur-sm ${className}`, children: [
|
|
28912
|
-
/* @__PURE__ */ jsx("div", { className: "p-4 border-b border-gray-50 bg-gradient-to-br from-slate-50/80 via-white to-blue-50/30", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
28913
|
-
/* @__PURE__ */
|
|
28914
|
-
|
|
28477
|
+
/* @__PURE__ */ jsx("div", { className: "p-4 border-b border-gray-50 bg-gradient-to-br from-slate-50/80 via-white to-blue-50/30", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
28478
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
28479
|
+
/* @__PURE__ */ jsx("div", { className: "mr-3", children: /* @__PURE__ */ jsx(Folder, { className: "h-5 w-5 text-slate-700" }) }),
|
|
28480
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-slate-900 tracking-tight", children: "Clips Explorer" }) })
|
|
28481
|
+
] }),
|
|
28482
|
+
/* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx(FilterDialogTrigger, {}) })
|
|
28915
28483
|
] }) }),
|
|
28916
|
-
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-slate-100/80", children: /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
28484
|
+
/* @__PURE__ */ jsx("div", { className: "hidden px-4 py-3 border-b border-slate-100/80", children: /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
28917
28485
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none", children: /* @__PURE__ */ jsx(Search, { className: "h-5 w-5 text-slate-400 group-focus-within:text-blue-500 transition-colors duration-200" }) }),
|
|
28918
28486
|
/* @__PURE__ */ jsx(
|
|
28919
28487
|
"input",
|
|
@@ -28927,7 +28495,7 @@ var FileManagerFilters = ({
|
|
|
28927
28495
|
),
|
|
28928
28496
|
searchTerm && /* @__PURE__ */ jsx("div", { className: "absolute inset-y-0 right-0 pr-4 flex items-center", children: /* @__PURE__ */ jsx("div", { className: "h-2 w-2 bg-blue-500 rounded-full animate-pulse" }) })
|
|
28929
28497
|
] }) }),
|
|
28930
|
-
/* @__PURE__ */ jsxs("div", { className: "px-4 py-3 h-[calc(100%-
|
|
28498
|
+
/* @__PURE__ */ jsxs("div", { className: "px-4 py-3 h-[calc(100%-8rem)] overflow-y-auto scrollbar-thin", children: [
|
|
28931
28499
|
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: filterTree.map((node) => renderNode(node)) }),
|
|
28932
28500
|
filterTree.length === 0 && searchTerm && /* @__PURE__ */ jsxs("div", { className: "text-center py-12", children: [
|
|
28933
28501
|
/* @__PURE__ */ jsx("div", { className: "text-slate-300 mb-4", children: /* @__PURE__ */ jsx(Search, { className: "h-12 w-12 mx-auto" }) }),
|
|
@@ -28959,6 +28527,301 @@ var FileManagerFilters = ({
|
|
|
28959
28527
|
] })
|
|
28960
28528
|
] });
|
|
28961
28529
|
};
|
|
28530
|
+
var PERCENTILE_PRESETS = [5, 10, 15, 20, 25, 30];
|
|
28531
|
+
var AdvancedFilterDialog = ({
|
|
28532
|
+
onApply
|
|
28533
|
+
}) => {
|
|
28534
|
+
const {
|
|
28535
|
+
state,
|
|
28536
|
+
updatePercentile,
|
|
28537
|
+
toggleClipType,
|
|
28538
|
+
resetFilters,
|
|
28539
|
+
toggleAdvancedPanel
|
|
28540
|
+
} = useClipFilter();
|
|
28541
|
+
const [customPercentile, setCustomPercentile] = useState(state.percentile.toString());
|
|
28542
|
+
useEffect(() => {
|
|
28543
|
+
setCustomPercentile(state.percentile.toString());
|
|
28544
|
+
}, [state.percentile]);
|
|
28545
|
+
const handlePercentileChange = (value) => {
|
|
28546
|
+
updatePercentile(value);
|
|
28547
|
+
setCustomPercentile(value.toString());
|
|
28548
|
+
};
|
|
28549
|
+
const handleCustomPercentileSubmit = () => {
|
|
28550
|
+
const value = parseInt(customPercentile);
|
|
28551
|
+
if (!isNaN(value) && value >= 1 && value <= 100) {
|
|
28552
|
+
updatePercentile(value);
|
|
28553
|
+
}
|
|
28554
|
+
};
|
|
28555
|
+
const handleClipTypeToggle = (clipType) => {
|
|
28556
|
+
toggleClipType(clipType);
|
|
28557
|
+
};
|
|
28558
|
+
const handleApply = () => {
|
|
28559
|
+
onApply?.();
|
|
28560
|
+
toggleAdvancedPanel();
|
|
28561
|
+
};
|
|
28562
|
+
const handleReset = () => {
|
|
28563
|
+
resetFilters();
|
|
28564
|
+
setCustomPercentile("10");
|
|
28565
|
+
};
|
|
28566
|
+
if (!state.isAdvancedPanelOpen) return null;
|
|
28567
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
28568
|
+
/* @__PURE__ */ jsx(
|
|
28569
|
+
"div",
|
|
28570
|
+
{
|
|
28571
|
+
className: "fixed inset-0 bg-black/30 backdrop-blur-sm z-50 transition-opacity duration-300",
|
|
28572
|
+
onClick: toggleAdvancedPanel
|
|
28573
|
+
}
|
|
28574
|
+
),
|
|
28575
|
+
/* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: /* @__PURE__ */ jsxs(
|
|
28576
|
+
"div",
|
|
28577
|
+
{
|
|
28578
|
+
className: "bg-white/95 backdrop-blur-xl rounded-2xl shadow-2xl border border-white/20 w-full max-w-2xl max-h-[80vh] overflow-hidden transform transition-all duration-300 scale-100",
|
|
28579
|
+
onClick: (e) => e.stopPropagation(),
|
|
28580
|
+
children: [
|
|
28581
|
+
/* @__PURE__ */ jsx("div", { className: "bg-gradient-to-r from-blue-50 via-indigo-50 to-purple-50 px-6 py-4 border-b border-gray-100/80", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
28582
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
|
|
28583
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 bg-blue-100 rounded-xl", children: /* @__PURE__ */ jsx(Sliders, { className: "h-6 w-6 text-blue-600" }) }),
|
|
28584
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
28585
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-bold text-gray-900", children: "Clip Filters" }),
|
|
28586
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "Customize your clip exploration experience" })
|
|
28587
|
+
] })
|
|
28588
|
+
] }),
|
|
28589
|
+
/* @__PURE__ */ jsx(
|
|
28590
|
+
"button",
|
|
28591
|
+
{
|
|
28592
|
+
onClick: toggleAdvancedPanel,
|
|
28593
|
+
className: "p-2 hover:bg-white/50 rounded-xl transition-colors",
|
|
28594
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5 text-gray-500" })
|
|
28595
|
+
}
|
|
28596
|
+
)
|
|
28597
|
+
] }) }),
|
|
28598
|
+
/* @__PURE__ */ jsxs("div", { className: "p-6 space-y-8 overflow-y-auto max-h-[60vh]", children: [
|
|
28599
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
28600
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
|
|
28601
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 bg-gradient-to-r from-emerald-100 to-blue-100 rounded-lg", children: /* @__PURE__ */ jsx(Activity, { className: "h-5 w-5 text-emerald-600" }) }),
|
|
28602
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
28603
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900", children: "Performance Percentile" }),
|
|
28604
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "Filter clips by performance ranking" })
|
|
28605
|
+
] })
|
|
28606
|
+
] }),
|
|
28607
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-r from-gray-50 to-blue-50 rounded-xl p-6 space-y-4", children: [
|
|
28608
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
28609
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
28610
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700", children: "Percentile Range" }),
|
|
28611
|
+
/* @__PURE__ */ jsx("div", { className: "bg-white px-3 py-1.5 rounded-lg border border-blue-200", children: /* @__PURE__ */ jsxs("span", { className: "text-lg font-bold text-blue-600", children: [
|
|
28612
|
+
"Top ",
|
|
28613
|
+
state.percentile,
|
|
28614
|
+
"%"
|
|
28615
|
+
] }) })
|
|
28616
|
+
] }),
|
|
28617
|
+
/* @__PURE__ */ jsx(
|
|
28618
|
+
"input",
|
|
28619
|
+
{
|
|
28620
|
+
type: "range",
|
|
28621
|
+
min: "5",
|
|
28622
|
+
max: "50",
|
|
28623
|
+
step: "1",
|
|
28624
|
+
value: state.percentile,
|
|
28625
|
+
onChange: (e) => handlePercentileChange(parseInt(e.target.value)),
|
|
28626
|
+
className: "w-full h-3 bg-gradient-to-r from-green-200 via-yellow-200 to-red-200 rounded-lg appearance-none cursor-pointer",
|
|
28627
|
+
style: {
|
|
28628
|
+
background: `linear-gradient(to right, #86efac 0%, #fde047 50%, #fca5a5 100%)`
|
|
28629
|
+
}
|
|
28630
|
+
}
|
|
28631
|
+
),
|
|
28632
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-xs text-gray-500", children: [
|
|
28633
|
+
/* @__PURE__ */ jsx("span", { children: "5% (Best)" }),
|
|
28634
|
+
/* @__PURE__ */ jsx("span", { children: "25% (Average)" }),
|
|
28635
|
+
/* @__PURE__ */ jsx("span", { children: "50% (Worst)" })
|
|
28636
|
+
] })
|
|
28637
|
+
] }),
|
|
28638
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
28639
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700", children: "Quick Presets" }),
|
|
28640
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: PERCENTILE_PRESETS.map((preset) => /* @__PURE__ */ jsxs(
|
|
28641
|
+
"button",
|
|
28642
|
+
{
|
|
28643
|
+
onClick: () => handlePercentileChange(preset),
|
|
28644
|
+
className: `px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200 ${state.percentile === preset ? "bg-blue-600 text-white shadow-lg shadow-blue-200 scale-105" : "bg-white text-gray-700 border border-gray-200 hover:bg-blue-50 hover:border-blue-300"}`,
|
|
28645
|
+
children: [
|
|
28646
|
+
"Top ",
|
|
28647
|
+
preset,
|
|
28648
|
+
"%"
|
|
28649
|
+
]
|
|
28650
|
+
},
|
|
28651
|
+
preset
|
|
28652
|
+
)) })
|
|
28653
|
+
] }),
|
|
28654
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
|
|
28655
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700", children: "Custom:" }),
|
|
28656
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
28657
|
+
/* @__PURE__ */ jsx(
|
|
28658
|
+
"input",
|
|
28659
|
+
{
|
|
28660
|
+
type: "number",
|
|
28661
|
+
min: "1",
|
|
28662
|
+
max: "100",
|
|
28663
|
+
value: customPercentile,
|
|
28664
|
+
onChange: (e) => setCustomPercentile(e.target.value),
|
|
28665
|
+
onKeyPress: (e) => e.key === "Enter" && handleCustomPercentileSubmit(),
|
|
28666
|
+
className: "w-20 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
|
|
28667
|
+
placeholder: "10"
|
|
28668
|
+
}
|
|
28669
|
+
),
|
|
28670
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-600", children: "%" }),
|
|
28671
|
+
/* @__PURE__ */ jsx(
|
|
28672
|
+
"button",
|
|
28673
|
+
{
|
|
28674
|
+
onClick: handleCustomPercentileSubmit,
|
|
28675
|
+
className: "px-3 py-2 text-sm font-medium bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors",
|
|
28676
|
+
children: "Apply"
|
|
28677
|
+
}
|
|
28678
|
+
)
|
|
28679
|
+
] })
|
|
28680
|
+
] })
|
|
28681
|
+
] })
|
|
28682
|
+
] }),
|
|
28683
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
28684
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
|
|
28685
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 bg-gradient-to-r from-purple-100 to-pink-100 rounded-lg", children: /* @__PURE__ */ jsx(Layers, { className: "h-5 w-5 text-purple-600" }) }),
|
|
28686
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
28687
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900", children: "Clip Categories" }),
|
|
28688
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-600", children: "Choose which types of clips to display" })
|
|
28689
|
+
] })
|
|
28690
|
+
] }),
|
|
28691
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [
|
|
28692
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-br from-green-50 to-red-50 rounded-xl p-4 space-y-3", children: [
|
|
28693
|
+
/* @__PURE__ */ jsxs("h4", { className: "text-sm font-bold text-gray-700 uppercase tracking-wide flex items-center space-x-2", children: [
|
|
28694
|
+
/* @__PURE__ */ jsx(Sparkles, { className: "h-4 w-4 text-emerald-600" }),
|
|
28695
|
+
/* @__PURE__ */ jsx("span", { children: "Performance Extremes" })
|
|
28696
|
+
] }),
|
|
28697
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
28698
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-3 cursor-pointer group", children: [
|
|
28699
|
+
/* @__PURE__ */ jsx(
|
|
28700
|
+
"input",
|
|
28701
|
+
{
|
|
28702
|
+
type: "checkbox",
|
|
28703
|
+
checked: state.showFastCycles,
|
|
28704
|
+
onChange: () => handleClipTypeToggle("showFastCycles"),
|
|
28705
|
+
className: "w-4 h-4 text-green-600 border-gray-300 rounded focus:ring-green-500"
|
|
28706
|
+
}
|
|
28707
|
+
),
|
|
28708
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
28709
|
+
/* @__PURE__ */ jsx(TrendingUp, { className: "h-4 w-4 text-green-600" }),
|
|
28710
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 group-hover:text-green-700", children: "Fast Cycles" })
|
|
28711
|
+
] })
|
|
28712
|
+
] }),
|
|
28713
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-3 cursor-pointer group", children: [
|
|
28714
|
+
/* @__PURE__ */ jsx(
|
|
28715
|
+
"input",
|
|
28716
|
+
{
|
|
28717
|
+
type: "checkbox",
|
|
28718
|
+
checked: state.showSlowCycles,
|
|
28719
|
+
onChange: () => handleClipTypeToggle("showSlowCycles"),
|
|
28720
|
+
className: "w-4 h-4 text-red-600 border-gray-300 rounded focus:ring-red-500"
|
|
28721
|
+
}
|
|
28722
|
+
),
|
|
28723
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
28724
|
+
/* @__PURE__ */ jsx(TrendingDown, { className: "h-4 w-4 text-red-600" }),
|
|
28725
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 group-hover:text-red-700", children: "Slow Cycles" })
|
|
28726
|
+
] })
|
|
28727
|
+
] }),
|
|
28728
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-3 cursor-pointer group", children: [
|
|
28729
|
+
/* @__PURE__ */ jsx(
|
|
28730
|
+
"input",
|
|
28731
|
+
{
|
|
28732
|
+
type: "checkbox",
|
|
28733
|
+
checked: state.showLongestIdles,
|
|
28734
|
+
onChange: () => handleClipTypeToggle("showLongestIdles"),
|
|
28735
|
+
className: "w-4 h-4 text-orange-600 border-gray-300 rounded focus:ring-orange-500"
|
|
28736
|
+
}
|
|
28737
|
+
),
|
|
28738
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
28739
|
+
/* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-orange-600" }),
|
|
28740
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 group-hover:text-orange-700", children: "Longest Idle Times" })
|
|
28741
|
+
] })
|
|
28742
|
+
] })
|
|
28743
|
+
] })
|
|
28744
|
+
] }),
|
|
28745
|
+
/* @__PURE__ */ jsxs("div", { className: "bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl p-4 space-y-3", children: [
|
|
28746
|
+
/* @__PURE__ */ jsxs("h4", { className: "text-sm font-bold text-gray-700 uppercase tracking-wide flex items-center space-x-2", children: [
|
|
28747
|
+
/* @__PURE__ */ jsx(CheckCircle2, { className: "h-4 w-4 text-blue-600" }),
|
|
28748
|
+
/* @__PURE__ */ jsx("span", { children: "Standard Categories" })
|
|
28749
|
+
] }),
|
|
28750
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
28751
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-3 cursor-pointer group", children: [
|
|
28752
|
+
/* @__PURE__ */ jsx(
|
|
28753
|
+
"input",
|
|
28754
|
+
{
|
|
28755
|
+
type: "checkbox",
|
|
28756
|
+
checked: state.showCycleCompletion,
|
|
28757
|
+
onChange: () => handleClipTypeToggle("showCycleCompletion"),
|
|
28758
|
+
className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
|
|
28759
|
+
}
|
|
28760
|
+
),
|
|
28761
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
28762
|
+
/* @__PURE__ */ jsx(CheckCircle2, { className: "h-4 w-4 text-blue-600" }),
|
|
28763
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 group-hover:text-blue-700", children: "Cycle Completion" })
|
|
28764
|
+
] })
|
|
28765
|
+
] }),
|
|
28766
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-3 cursor-pointer group", children: [
|
|
28767
|
+
/* @__PURE__ */ jsx(
|
|
28768
|
+
"input",
|
|
28769
|
+
{
|
|
28770
|
+
type: "checkbox",
|
|
28771
|
+
checked: state.showIdleTime,
|
|
28772
|
+
onChange: () => handleClipTypeToggle("showIdleTime"),
|
|
28773
|
+
className: "w-4 h-4 text-purple-600 border-gray-300 rounded focus:ring-purple-500"
|
|
28774
|
+
}
|
|
28775
|
+
),
|
|
28776
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
28777
|
+
/* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-purple-600" }),
|
|
28778
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-700 group-hover:text-purple-700", children: "Idle Time" })
|
|
28779
|
+
] })
|
|
28780
|
+
] })
|
|
28781
|
+
] })
|
|
28782
|
+
] })
|
|
28783
|
+
] })
|
|
28784
|
+
] })
|
|
28785
|
+
] }),
|
|
28786
|
+
/* @__PURE__ */ jsx("div", { className: "bg-gray-50/80 px-6 py-4 border-t border-gray-100/80", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
28787
|
+
/* @__PURE__ */ jsxs(
|
|
28788
|
+
"button",
|
|
28789
|
+
{
|
|
28790
|
+
onClick: handleReset,
|
|
28791
|
+
className: "flex items-center space-x-2 px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors",
|
|
28792
|
+
children: [
|
|
28793
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4" }),
|
|
28794
|
+
/* @__PURE__ */ jsx("span", { children: "Reset All Filters" })
|
|
28795
|
+
]
|
|
28796
|
+
}
|
|
28797
|
+
),
|
|
28798
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
28799
|
+
/* @__PURE__ */ jsx(
|
|
28800
|
+
"button",
|
|
28801
|
+
{
|
|
28802
|
+
onClick: toggleAdvancedPanel,
|
|
28803
|
+
className: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors",
|
|
28804
|
+
children: "Cancel"
|
|
28805
|
+
}
|
|
28806
|
+
),
|
|
28807
|
+
/* @__PURE__ */ jsxs(
|
|
28808
|
+
"button",
|
|
28809
|
+
{
|
|
28810
|
+
onClick: handleApply,
|
|
28811
|
+
className: "flex items-center space-x-2 px-6 py-2 text-sm font-medium text-white bg-gradient-to-r from-blue-600 to-indigo-600 rounded-lg hover:from-blue-700 hover:to-indigo-700 shadow-lg shadow-blue-200 transition-all duration-200",
|
|
28812
|
+
children: [
|
|
28813
|
+
/* @__PURE__ */ jsx(Filter, { className: "h-4 w-4" }),
|
|
28814
|
+
/* @__PURE__ */ jsx("span", { children: "Apply Filters" })
|
|
28815
|
+
]
|
|
28816
|
+
}
|
|
28817
|
+
)
|
|
28818
|
+
] })
|
|
28819
|
+
] }) })
|
|
28820
|
+
]
|
|
28821
|
+
}
|
|
28822
|
+
) })
|
|
28823
|
+
] });
|
|
28824
|
+
};
|
|
28962
28825
|
function useClipsRealtimeUpdates({
|
|
28963
28826
|
workspaceId,
|
|
28964
28827
|
date,
|
|
@@ -29060,8 +28923,6 @@ function useClipsRealtimeUpdates({
|
|
|
29060
28923
|
hasNewClips: newClipsNotification !== null && newClipsNotification.count > 0
|
|
29061
28924
|
};
|
|
29062
28925
|
}
|
|
29063
|
-
var USE_SUPABASE_CLIPS2 = true;
|
|
29064
|
-
var S3ClipsService3 = S3ClipsSupabaseService ;
|
|
29065
28926
|
var BottlenecksContent = ({
|
|
29066
28927
|
workspaceId,
|
|
29067
28928
|
workspaceName,
|
|
@@ -29119,8 +28980,8 @@ var BottlenecksContent = ({
|
|
|
29119
28980
|
workspaceId,
|
|
29120
28981
|
date: date || getOperationalDate(),
|
|
29121
28982
|
shiftId: effectiveShift,
|
|
29122
|
-
enabled:
|
|
29123
|
-
//
|
|
28983
|
+
enabled: true,
|
|
28984
|
+
// Supabase implementation
|
|
29124
28985
|
onNewClips: (notification) => {
|
|
29125
28986
|
console.log(`[BottlenecksContent] New clips detected:`, notification);
|
|
29126
28987
|
if (notification.clips.length > 0) {
|
|
@@ -29145,7 +29006,7 @@ var BottlenecksContent = ({
|
|
|
29145
29006
|
console.warn("S3 configuration not found in dashboard config");
|
|
29146
29007
|
return null;
|
|
29147
29008
|
}
|
|
29148
|
-
return new
|
|
29009
|
+
return new S3ClipsSupabaseService(dashboardConfig);
|
|
29149
29010
|
}, [dashboardConfig]);
|
|
29150
29011
|
const {
|
|
29151
29012
|
clipTypes,
|
|
@@ -29166,8 +29027,7 @@ var BottlenecksContent = ({
|
|
|
29166
29027
|
dynamicCounts,
|
|
29167
29028
|
workspaceId,
|
|
29168
29029
|
date: date || getOperationalDate(),
|
|
29169
|
-
shift: shift || "0"
|
|
29170
|
-
USE_SUPABASE_CLIPS: USE_SUPABASE_CLIPS2
|
|
29030
|
+
shift: shift || "0"
|
|
29171
29031
|
});
|
|
29172
29032
|
useEffect(() => {
|
|
29173
29033
|
if (clipTypes.length > 0 && !initialFilter) {
|
|
@@ -29275,12 +29135,8 @@ var BottlenecksContent = ({
|
|
|
29275
29135
|
operationalDate,
|
|
29276
29136
|
shiftStr,
|
|
29277
29137
|
targetCategory,
|
|
29278
|
-
0
|
|
29138
|
+
0
|
|
29279
29139
|
// First video (index 0)
|
|
29280
|
-
true,
|
|
29281
|
-
// includeCycleTime
|
|
29282
|
-
true
|
|
29283
|
-
// includeMetadata
|
|
29284
29140
|
);
|
|
29285
29141
|
if (firstVideo && isMountedRef.current) {
|
|
29286
29142
|
console.log(`[BottlenecksContent] Successfully loaded first video via index`);
|
|
@@ -29366,18 +29222,46 @@ var BottlenecksContent = ({
|
|
|
29366
29222
|
}
|
|
29367
29223
|
}
|
|
29368
29224
|
}, [activeFilter, allVideos, mergedCounts]);
|
|
29225
|
+
const isPercentileCategory = useCallback((categoryId) => {
|
|
29226
|
+
return ["fast-cycles", "slow-cycles", "longest-idles"].includes(categoryId);
|
|
29227
|
+
}, []);
|
|
29228
|
+
const getClipTypesForPercentileCategory = useCallback((categoryId) => {
|
|
29229
|
+
switch (categoryId) {
|
|
29230
|
+
case "fast-cycles":
|
|
29231
|
+
case "slow-cycles":
|
|
29232
|
+
return ["cycle_completion"];
|
|
29233
|
+
// Only cycle_completion now (best/worst/long moved to 'other')
|
|
29234
|
+
case "longest-idles":
|
|
29235
|
+
return ["idle_time"];
|
|
29236
|
+
default:
|
|
29237
|
+
return [categoryId];
|
|
29238
|
+
}
|
|
29239
|
+
}, []);
|
|
29240
|
+
const getCategoryCount = useCallback((categoryId) => {
|
|
29241
|
+
if (isPercentileCategory(categoryId)) {
|
|
29242
|
+
if (isPercentileCategory(activeFilter) && activeFilter === categoryId) {
|
|
29243
|
+
return allVideos.length;
|
|
29244
|
+
}
|
|
29245
|
+
return 0;
|
|
29246
|
+
}
|
|
29247
|
+
return mergedCounts[categoryId] || 0;
|
|
29248
|
+
}, [isPercentileCategory, allVideos, mergedCounts, activeFilter]);
|
|
29369
29249
|
const filteredVideos = useMemo(() => {
|
|
29370
29250
|
if (!allVideos) return [];
|
|
29371
29251
|
let filtered = [];
|
|
29372
29252
|
if (activeFilter === "all") {
|
|
29373
29253
|
filtered = [...allVideos];
|
|
29254
|
+
} else if (isPercentileCategory(activeFilter)) {
|
|
29255
|
+
const allowedTypes = getClipTypesForPercentileCategory(activeFilter);
|
|
29256
|
+
filtered = allVideos.filter((video) => allowedTypes.includes(video.type));
|
|
29257
|
+
console.log(`[BottlenecksContent] Filtering percentile category ${activeFilter}: found ${filtered.length} videos of types [${allowedTypes.join(", ")}]`);
|
|
29374
29258
|
} else {
|
|
29375
29259
|
filtered = allVideos.filter((video) => video.type === activeFilter);
|
|
29376
29260
|
}
|
|
29377
29261
|
return filtered.sort((a, b) => {
|
|
29378
29262
|
return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
|
|
29379
29263
|
});
|
|
29380
|
-
}, [activeFilter, allVideos]);
|
|
29264
|
+
}, [activeFilter, allVideos, isPercentileCategory, getClipTypesForPercentileCategory]);
|
|
29381
29265
|
useEffect(() => {
|
|
29382
29266
|
if (isNavigating && currentIndex < filteredVideos.length) {
|
|
29383
29267
|
setIsNavigating(false);
|
|
@@ -29393,21 +29277,63 @@ var BottlenecksContent = ({
|
|
|
29393
29277
|
updateActiveFilter(categoryId);
|
|
29394
29278
|
}
|
|
29395
29279
|
try {
|
|
29396
|
-
|
|
29397
|
-
|
|
29398
|
-
|
|
29399
|
-
|
|
29400
|
-
|
|
29401
|
-
|
|
29402
|
-
|
|
29403
|
-
workspaceId,
|
|
29404
|
-
date || getOperationalDate(),
|
|
29405
|
-
effectiveShift
|
|
29280
|
+
if (isPercentileCategory(categoryId)) {
|
|
29281
|
+
console.log(`[BottlenecksContent] Loading percentile category: ${categoryId}`);
|
|
29282
|
+
const percentileType = categoryId === "fast-cycles" ? "fast-cycles" : categoryId === "slow-cycles" ? "slow-cycles" : "idle-times";
|
|
29283
|
+
const { createClient: createClient4 } = await import('@supabase/supabase-js');
|
|
29284
|
+
const supabase = createClient4(
|
|
29285
|
+
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
29286
|
+
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || ""
|
|
29406
29287
|
);
|
|
29407
|
-
|
|
29408
|
-
|
|
29409
|
-
|
|
29288
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
29289
|
+
const authToken = session?.access_token;
|
|
29290
|
+
if (!authToken) {
|
|
29291
|
+
throw new Error("Authentication required");
|
|
29292
|
+
}
|
|
29293
|
+
const response = await fetch("/api/clips/supabase", {
|
|
29294
|
+
method: "POST",
|
|
29295
|
+
headers: {
|
|
29296
|
+
"Content-Type": "application/json",
|
|
29297
|
+
"Authorization": `Bearer ${authToken}`
|
|
29298
|
+
},
|
|
29299
|
+
body: JSON.stringify({
|
|
29300
|
+
action: "percentile-clips",
|
|
29301
|
+
percentileAction: percentileType,
|
|
29302
|
+
workspaceId,
|
|
29303
|
+
startDate: `${date || getOperationalDate()}T00:00:00Z`,
|
|
29304
|
+
endDate: `${date || getOperationalDate()}T23:59:59Z`,
|
|
29305
|
+
percentile: 10,
|
|
29306
|
+
shiftId: effectiveShift,
|
|
29307
|
+
limit: 100
|
|
29308
|
+
})
|
|
29309
|
+
});
|
|
29310
|
+
if (!response.ok) {
|
|
29311
|
+
const errorData = await response.json();
|
|
29312
|
+
throw new Error(errorData.error || `API error: ${response.status}`);
|
|
29313
|
+
}
|
|
29314
|
+
const percentileData = await response.json();
|
|
29315
|
+
if (percentileData.clips && percentileData.clips.length > 0 && isMountedRef.current) {
|
|
29316
|
+
const clickedClipIndex = percentileData.clips.findIndex((clip) => clip.id === clipId);
|
|
29317
|
+
if (clickedClipIndex !== -1) {
|
|
29318
|
+
setCurrentClipId(clipId);
|
|
29319
|
+
setAllVideos(percentileData.clips);
|
|
29320
|
+
setCurrentIndex(clickedClipIndex);
|
|
29321
|
+
setCategoryPosition(clickedClipIndex + 1);
|
|
29322
|
+
console.log(`[BottlenecksContent] Loaded ${percentileData.clips.length} ${categoryId} clips, playing #${clickedClipIndex + 1}`);
|
|
29323
|
+
}
|
|
29324
|
+
}
|
|
29325
|
+
} else {
|
|
29326
|
+
const video = await s3ClipsService.getClipById(clipId);
|
|
29327
|
+
if (video && isMountedRef.current) {
|
|
29328
|
+
console.log(`[BottlenecksContent] Processing loaded video by ID: ${video.id}`);
|
|
29329
|
+
setCurrentClipId(clipId);
|
|
29330
|
+
setAllVideos([video]);
|
|
29331
|
+
setCurrentIndex(0);
|
|
29332
|
+
setCategoryPosition(1);
|
|
29333
|
+
console.log(`[BottlenecksContent] Successfully loaded clip by ID: ${video.id}`);
|
|
29334
|
+
}
|
|
29410
29335
|
}
|
|
29336
|
+
setIsNavigating(false);
|
|
29411
29337
|
} catch (error2) {
|
|
29412
29338
|
console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
|
|
29413
29339
|
if (isMountedRef.current) {
|
|
@@ -29427,9 +29353,7 @@ var BottlenecksContent = ({
|
|
|
29427
29353
|
operationalDate,
|
|
29428
29354
|
shiftStr,
|
|
29429
29355
|
categoryId,
|
|
29430
|
-
clipIndex
|
|
29431
|
-
true,
|
|
29432
|
-
true
|
|
29356
|
+
clipIndex
|
|
29433
29357
|
);
|
|
29434
29358
|
if (video?.id) {
|
|
29435
29359
|
await loadAndPlayClipById(video.id, categoryId);
|
|
@@ -29441,69 +29365,107 @@ var BottlenecksContent = ({
|
|
|
29441
29365
|
}
|
|
29442
29366
|
}, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById]);
|
|
29443
29367
|
const handleNext = useCallback(async () => {
|
|
29444
|
-
if (!isMountedRef.current
|
|
29368
|
+
if (!isMountedRef.current) return;
|
|
29445
29369
|
const currentFilter = activeFilterRef.current;
|
|
29446
|
-
console.log(`[handleNext] Navigating
|
|
29370
|
+
console.log(`[handleNext] Navigating in category: ${currentFilter}, current index: ${currentIndex}`);
|
|
29447
29371
|
setIsNavigating(true);
|
|
29448
29372
|
setError(null);
|
|
29449
29373
|
try {
|
|
29450
|
-
|
|
29451
|
-
|
|
29452
|
-
|
|
29453
|
-
|
|
29454
|
-
|
|
29455
|
-
|
|
29456
|
-
|
|
29457
|
-
|
|
29458
|
-
|
|
29459
|
-
|
|
29460
|
-
|
|
29461
|
-
|
|
29462
|
-
|
|
29463
|
-
setIsNavigating(false);
|
|
29464
|
-
videoRetryCountRef.current = 0;
|
|
29374
|
+
if (isPercentileCategory(currentFilter)) {
|
|
29375
|
+
if (currentIndex < allVideos.length - 1) {
|
|
29376
|
+
const nextIndex = currentIndex + 1;
|
|
29377
|
+
const nextVideo = allVideos[nextIndex];
|
|
29378
|
+
console.log(`[handleNext] Moving to next percentile clip: ${nextVideo.id} (index ${nextIndex})`);
|
|
29379
|
+
setCurrentClipId(nextVideo.id);
|
|
29380
|
+
setCurrentIndex(nextIndex);
|
|
29381
|
+
setCategoryPosition(nextIndex + 1);
|
|
29382
|
+
setIsNavigating(false);
|
|
29383
|
+
} else {
|
|
29384
|
+
console.log(`[handleNext] Already at last clip in percentile category`);
|
|
29385
|
+
setIsNavigating(false);
|
|
29386
|
+
}
|
|
29465
29387
|
} else {
|
|
29466
|
-
|
|
29467
|
-
|
|
29388
|
+
if (!currentClipId || !s3ClipsService) {
|
|
29389
|
+
setIsNavigating(false);
|
|
29390
|
+
return;
|
|
29391
|
+
}
|
|
29392
|
+
const neighbors = await s3ClipsService.getNeighboringClips(
|
|
29393
|
+
workspaceId,
|
|
29394
|
+
date || getOperationalDate(),
|
|
29395
|
+
effectiveShift,
|
|
29396
|
+
currentFilter,
|
|
29397
|
+
currentClipId
|
|
29398
|
+
);
|
|
29399
|
+
if (neighbors.next) {
|
|
29400
|
+
console.log(`[handleNext] Found next clip: ${neighbors.next.id}`);
|
|
29401
|
+
setCurrentClipId(neighbors.next.id || null);
|
|
29402
|
+
setAllVideos([neighbors.next]);
|
|
29403
|
+
setCurrentIndex(0);
|
|
29404
|
+
setCategoryPosition(categoryPosition + 1);
|
|
29405
|
+
setIsNavigating(false);
|
|
29406
|
+
videoRetryCountRef.current = 0;
|
|
29407
|
+
} else {
|
|
29408
|
+
console.log(`[handleNext] No next clip available`);
|
|
29409
|
+
setIsNavigating(false);
|
|
29410
|
+
}
|
|
29468
29411
|
}
|
|
29469
29412
|
} catch (error2) {
|
|
29470
|
-
console.error(`[handleNext] Error
|
|
29413
|
+
console.error(`[handleNext] Error navigating:`, error2);
|
|
29471
29414
|
setError("Failed to navigate to next clip");
|
|
29472
29415
|
setIsNavigating(false);
|
|
29473
29416
|
}
|
|
29474
|
-
}, [currentClipId, workspaceId, date, effectiveShift, s3ClipsService, categoryPosition]);
|
|
29417
|
+
}, [currentIndex, allVideos, currentClipId, workspaceId, date, effectiveShift, s3ClipsService, categoryPosition, isPercentileCategory]);
|
|
29475
29418
|
const handlePrevious = useCallback(async () => {
|
|
29476
|
-
if (!isMountedRef.current
|
|
29419
|
+
if (!isMountedRef.current) return;
|
|
29477
29420
|
const currentFilter = activeFilterRef.current;
|
|
29478
|
-
console.log(`[handlePrevious] Navigating
|
|
29421
|
+
console.log(`[handlePrevious] Navigating in category: ${currentFilter}, current index: ${currentIndex}`);
|
|
29479
29422
|
setIsNavigating(true);
|
|
29480
29423
|
setError(null);
|
|
29481
29424
|
try {
|
|
29482
|
-
|
|
29483
|
-
|
|
29484
|
-
|
|
29485
|
-
|
|
29486
|
-
|
|
29487
|
-
|
|
29488
|
-
|
|
29489
|
-
|
|
29490
|
-
|
|
29491
|
-
|
|
29492
|
-
|
|
29493
|
-
|
|
29494
|
-
|
|
29495
|
-
setIsNavigating(false);
|
|
29496
|
-
videoRetryCountRef.current = 0;
|
|
29425
|
+
if (isPercentileCategory(currentFilter)) {
|
|
29426
|
+
if (currentIndex > 0) {
|
|
29427
|
+
const prevIndex = currentIndex - 1;
|
|
29428
|
+
const prevVideo = allVideos[prevIndex];
|
|
29429
|
+
console.log(`[handlePrevious] Moving to previous percentile clip: ${prevVideo.id} (index ${prevIndex})`);
|
|
29430
|
+
setCurrentClipId(prevVideo.id);
|
|
29431
|
+
setCurrentIndex(prevIndex);
|
|
29432
|
+
setCategoryPosition(prevIndex + 1);
|
|
29433
|
+
setIsNavigating(false);
|
|
29434
|
+
} else {
|
|
29435
|
+
console.log(`[handlePrevious] Already at first clip in percentile category`);
|
|
29436
|
+
setIsNavigating(false);
|
|
29437
|
+
}
|
|
29497
29438
|
} else {
|
|
29498
|
-
|
|
29499
|
-
|
|
29439
|
+
if (!currentClipId || !s3ClipsService) {
|
|
29440
|
+
setIsNavigating(false);
|
|
29441
|
+
return;
|
|
29442
|
+
}
|
|
29443
|
+
const neighbors = await s3ClipsService.getNeighboringClips(
|
|
29444
|
+
workspaceId,
|
|
29445
|
+
date || getOperationalDate(),
|
|
29446
|
+
effectiveShift,
|
|
29447
|
+
currentFilter,
|
|
29448
|
+
currentClipId
|
|
29449
|
+
);
|
|
29450
|
+
if (neighbors.previous) {
|
|
29451
|
+
console.log(`[handlePrevious] Found previous clip: ${neighbors.previous.id}`);
|
|
29452
|
+
setCurrentClipId(neighbors.previous.id || null);
|
|
29453
|
+
setAllVideos([neighbors.previous]);
|
|
29454
|
+
setCurrentIndex(0);
|
|
29455
|
+
setCategoryPosition(Math.max(1, categoryPosition - 1));
|
|
29456
|
+
setIsNavigating(false);
|
|
29457
|
+
videoRetryCountRef.current = 0;
|
|
29458
|
+
} else {
|
|
29459
|
+
console.log(`[handlePrevious] No previous clip available`);
|
|
29460
|
+
setIsNavigating(false);
|
|
29461
|
+
}
|
|
29500
29462
|
}
|
|
29501
29463
|
} catch (error2) {
|
|
29502
|
-
console.error(`[handlePrevious] Error
|
|
29464
|
+
console.error(`[handlePrevious] Error navigating:`, error2);
|
|
29503
29465
|
setError("Failed to navigate to previous clip");
|
|
29504
29466
|
setIsNavigating(false);
|
|
29505
29467
|
}
|
|
29506
|
-
}, [currentClipId, workspaceId, date, effectiveShift, s3ClipsService, categoryPosition]);
|
|
29468
|
+
}, [currentIndex, allVideos, currentClipId, workspaceId, date, effectiveShift, s3ClipsService, categoryPosition, isPercentileCategory]);
|
|
29507
29469
|
const currentVideo = useMemo(() => {
|
|
29508
29470
|
if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
|
|
29509
29471
|
return null;
|
|
@@ -29617,24 +29579,36 @@ var BottlenecksContent = ({
|
|
|
29617
29579
|
};
|
|
29618
29580
|
const getClipTypeLabel = (video) => {
|
|
29619
29581
|
if (!video) return "";
|
|
29582
|
+
const currentFilter = activeFilterRef.current;
|
|
29583
|
+
if (isPercentileCategory(currentFilter)) {
|
|
29584
|
+
const percentileValue = video.percentile?.toFixed(1);
|
|
29585
|
+
switch (currentFilter) {
|
|
29586
|
+
case "fast-cycles":
|
|
29587
|
+
return `\u26A1 Fast Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
|
|
29588
|
+
case "slow-cycles":
|
|
29589
|
+
return `\u{1F40C} Slow Cycle ${percentileValue ? `(${percentileValue}%)` : ""}`;
|
|
29590
|
+
case "longest-idles":
|
|
29591
|
+
return `\u23F0 Long Idle ${percentileValue ? `(${percentileValue}%)` : ""}`;
|
|
29592
|
+
default:
|
|
29593
|
+
return video.description || "Performance Clip";
|
|
29594
|
+
}
|
|
29595
|
+
}
|
|
29620
29596
|
switch (video.type) {
|
|
29621
29597
|
case "low_value":
|
|
29622
29598
|
return "Idle Moment";
|
|
29623
29599
|
case "missing_quality_check":
|
|
29624
29600
|
return "SOP Deviation";
|
|
29625
|
-
case "best_cycle_time":
|
|
29626
|
-
return "Best Cycle Time";
|
|
29627
|
-
case "worst_cycle_time":
|
|
29628
|
-
return "Worst Cycle Time";
|
|
29629
29601
|
case "cycle_completion":
|
|
29630
29602
|
return "Cycle Completion";
|
|
29603
|
+
case "idle_time":
|
|
29604
|
+
return "Idle Time";
|
|
29631
29605
|
case "running_cycle":
|
|
29632
29606
|
return "Running Cycle";
|
|
29633
29607
|
case "setup_state":
|
|
29634
29608
|
return "Setup State";
|
|
29635
29609
|
case "bottleneck":
|
|
29636
29610
|
default:
|
|
29637
|
-
return "";
|
|
29611
|
+
return video.description || "";
|
|
29638
29612
|
}
|
|
29639
29613
|
};
|
|
29640
29614
|
if (!dashboardConfig?.s3Config) {
|
|
@@ -29690,22 +29664,22 @@ var BottlenecksContent = ({
|
|
|
29690
29664
|
"button",
|
|
29691
29665
|
{
|
|
29692
29666
|
onClick: handlePrevious,
|
|
29693
|
-
disabled: categoryPosition <= 1 || (
|
|
29694
|
-
className: `p-2 rounded-full transition-colors ${categoryPosition <= 1 || (
|
|
29667
|
+
disabled: categoryPosition <= 1 || getCategoryCount(activeFilter) === 0,
|
|
29668
|
+
className: `p-2 rounded-full transition-colors ${categoryPosition <= 1 || getCategoryCount(activeFilter) === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"}`,
|
|
29695
29669
|
"aria-label": "Previous video",
|
|
29696
29670
|
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
|
|
29697
29671
|
}
|
|
29698
29672
|
),
|
|
29699
29673
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
29700
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children: (
|
|
29674
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children: getCategoryCount(activeFilter) > 0 ? `${categoryPosition} / ${getCategoryCount(activeFilter)}` : "0 / 0" }),
|
|
29701
29675
|
error && /* @__PURE__ */ jsx("span", { className: "text-xs text-red-600 font-medium", children: error })
|
|
29702
29676
|
] }),
|
|
29703
29677
|
/* @__PURE__ */ jsx(
|
|
29704
29678
|
"button",
|
|
29705
29679
|
{
|
|
29706
29680
|
onClick: handleNext,
|
|
29707
|
-
disabled: categoryPosition >= (
|
|
29708
|
-
className: `p-2 rounded-full transition-colors ${categoryPosition >= (
|
|
29681
|
+
disabled: categoryPosition >= getCategoryCount(activeFilter) || getCategoryCount(activeFilter) === 0,
|
|
29682
|
+
className: `p-2 rounded-full transition-colors ${categoryPosition >= getCategoryCount(activeFilter) || getCategoryCount(activeFilter) === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"}`,
|
|
29709
29683
|
"aria-label": "Next video",
|
|
29710
29684
|
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
|
|
29711
29685
|
}
|
|
@@ -29776,9 +29750,9 @@ var BottlenecksContent = ({
|
|
|
29776
29750
|
}
|
|
29777
29751
|
)
|
|
29778
29752
|
] }) }),
|
|
29779
|
-
(currentVideo.type === "
|
|
29780
|
-
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" ? "bg-purple-400" :
|
|
29781
|
-
(currentVideo.type === "
|
|
29753
|
+
(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" ? /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
29754
|
+
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" || currentVideo.type === "idle_time" ? "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` }),
|
|
29755
|
+
(currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds ? /* @__PURE__ */ jsxs("span", { className: "opacity-90 font-mono bg-black/30 px-2 py-0.5 rounded", children: [
|
|
29782
29756
|
"Cycle time: ",
|
|
29783
29757
|
currentVideo.cycle_time_seconds.toFixed(1),
|
|
29784
29758
|
"s"
|
|
@@ -29908,7 +29882,15 @@ var BottlenecksContent = ({
|
|
|
29908
29882
|
className: "h-full"
|
|
29909
29883
|
}
|
|
29910
29884
|
) })
|
|
29911
|
-
] })
|
|
29885
|
+
] }),
|
|
29886
|
+
/* @__PURE__ */ jsx(
|
|
29887
|
+
AdvancedFilterDialog,
|
|
29888
|
+
{
|
|
29889
|
+
onApply: () => {
|
|
29890
|
+
console.log("[BottlenecksContent] Advanced filters applied, will refresh clips...");
|
|
29891
|
+
}
|
|
29892
|
+
}
|
|
29893
|
+
)
|
|
29912
29894
|
] });
|
|
29913
29895
|
};
|
|
29914
29896
|
var getEfficiencyColor = (efficiency) => {
|
|
@@ -30011,7 +29993,7 @@ var arePropsEqual = (prevProps, nextProps) => {
|
|
|
30011
29993
|
return prevProps.data.efficiency === nextProps.data.efficiency && prevProps.data.trend_score === nextProps.data.trend_score && prevProps.data.workspace_id === nextProps.data.workspace_id && prevProps.data.workspace_name === nextProps.data.workspace_name && prevProps.isBottleneck === nextProps.isBottleneck && prevProps.isLowEfficiency === nextProps.isLowEfficiency && prevProps.isVeryLowEfficiency === nextProps.isVeryLowEfficiency && // Position doesn't need deep equality check as it's generally static
|
|
30012
29994
|
prevProps.position.id === nextProps.position.id;
|
|
30013
29995
|
};
|
|
30014
|
-
var WorkspaceGridItem =
|
|
29996
|
+
var WorkspaceGridItem = React20__default.memo(({
|
|
30015
29997
|
data,
|
|
30016
29998
|
position,
|
|
30017
29999
|
isBottleneck = false,
|
|
@@ -30104,7 +30086,7 @@ var WorkspaceGridItem = React19__default.memo(({
|
|
|
30104
30086
|
);
|
|
30105
30087
|
}, arePropsEqual);
|
|
30106
30088
|
WorkspaceGridItem.displayName = "WorkspaceGridItem";
|
|
30107
|
-
var WorkspaceGrid =
|
|
30089
|
+
var WorkspaceGrid = React20__default.memo(({
|
|
30108
30090
|
workspaces,
|
|
30109
30091
|
isPdfMode = false,
|
|
30110
30092
|
customWorkspacePositions,
|
|
@@ -30298,7 +30280,7 @@ var KPICard = ({
|
|
|
30298
30280
|
}) => {
|
|
30299
30281
|
useThemeConfig();
|
|
30300
30282
|
const { formatNumber } = useFormatNumber();
|
|
30301
|
-
const trendInfo =
|
|
30283
|
+
const trendInfo = React20__default.useMemo(() => {
|
|
30302
30284
|
let trendValue = trend || "neutral";
|
|
30303
30285
|
if (change !== void 0 && trend === void 0) {
|
|
30304
30286
|
trendValue = change > 0 ? "up" : change < 0 ? "down" : "neutral";
|
|
@@ -30321,7 +30303,7 @@ var KPICard = ({
|
|
|
30321
30303
|
const shouldShowTrend = !(change === 0 && trend === void 0);
|
|
30322
30304
|
return { trendValue, Icon: Icon2, colorClass, shouldShowTrend };
|
|
30323
30305
|
}, [trend, change]);
|
|
30324
|
-
const formattedValue =
|
|
30306
|
+
const formattedValue = React20__default.useMemo(() => {
|
|
30325
30307
|
if (title === "Quality Compliance" && typeof value === "number") {
|
|
30326
30308
|
return value.toFixed(1);
|
|
30327
30309
|
}
|
|
@@ -30335,7 +30317,7 @@ var KPICard = ({
|
|
|
30335
30317
|
}
|
|
30336
30318
|
return value;
|
|
30337
30319
|
}, [value, title]);
|
|
30338
|
-
const formattedChange =
|
|
30320
|
+
const formattedChange = React20__default.useMemo(() => {
|
|
30339
30321
|
if (change === void 0 || change === 0) return null;
|
|
30340
30322
|
const absChange = Math.abs(change);
|
|
30341
30323
|
return formatNumber(absChange, { minimumFractionDigits: 0, maximumFractionDigits: 1 });
|
|
@@ -31242,6 +31224,237 @@ var WorkspaceDisplayNameExample = () => {
|
|
|
31242
31224
|
] })
|
|
31243
31225
|
] });
|
|
31244
31226
|
};
|
|
31227
|
+
var AdvancedFilterPanel = ({
|
|
31228
|
+
className = "",
|
|
31229
|
+
onApply
|
|
31230
|
+
}) => {
|
|
31231
|
+
const {
|
|
31232
|
+
state,
|
|
31233
|
+
updatePercentile,
|
|
31234
|
+
toggleClipType,
|
|
31235
|
+
updateShiftFilter,
|
|
31236
|
+
resetFilters,
|
|
31237
|
+
toggleAdvancedPanel
|
|
31238
|
+
} = useClipFilter();
|
|
31239
|
+
const [customPercentile, setCustomPercentile] = useState(state.percentile.toString());
|
|
31240
|
+
const handlePercentileChange = (value) => {
|
|
31241
|
+
updatePercentile(value);
|
|
31242
|
+
setCustomPercentile(value.toString());
|
|
31243
|
+
onApply?.();
|
|
31244
|
+
};
|
|
31245
|
+
const handleShiftChange = (shiftId) => {
|
|
31246
|
+
updateShiftFilter(shiftId);
|
|
31247
|
+
onApply?.();
|
|
31248
|
+
};
|
|
31249
|
+
const handleClipTypeToggle = (clipType) => {
|
|
31250
|
+
toggleClipType(clipType);
|
|
31251
|
+
onApply?.();
|
|
31252
|
+
};
|
|
31253
|
+
const handleReset = () => {
|
|
31254
|
+
resetFilters();
|
|
31255
|
+
setCustomPercentile("10");
|
|
31256
|
+
onApply?.();
|
|
31257
|
+
};
|
|
31258
|
+
return /* @__PURE__ */ jsxs("div", { className: `bg-white rounded-xl shadow-lg border border-gray-100 ${className}`, children: [
|
|
31259
|
+
/* @__PURE__ */ jsx(
|
|
31260
|
+
"div",
|
|
31261
|
+
{
|
|
31262
|
+
className: "p-4 border-b border-gray-100 bg-gradient-to-r from-blue-50 to-indigo-50 cursor-pointer rounded-t-xl",
|
|
31263
|
+
onClick: toggleAdvancedPanel,
|
|
31264
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
31265
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
|
|
31266
|
+
/* @__PURE__ */ jsx(Sliders, { className: "h-5 w-5 text-blue-600" }),
|
|
31267
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-900", children: "Advanced Filters" })
|
|
31268
|
+
] }),
|
|
31269
|
+
/* @__PURE__ */ jsx("button", { className: "p-1 hover:bg-white/50 rounded-lg transition-colors", children: state.isAdvancedPanelOpen ? /* @__PURE__ */ jsx(ChevronUp, { className: "h-5 w-5 text-gray-600" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "h-5 w-5 text-gray-600" }) })
|
|
31270
|
+
] })
|
|
31271
|
+
}
|
|
31272
|
+
),
|
|
31273
|
+
state.isAdvancedPanelOpen && /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-3", children: [
|
|
31274
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
31275
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
|
|
31276
|
+
/* @__PURE__ */ jsx(Activity, { className: "h-4 w-4 text-blue-600" }),
|
|
31277
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-gray-700", children: "Percentile:" }),
|
|
31278
|
+
/* @__PURE__ */ jsx(
|
|
31279
|
+
"input",
|
|
31280
|
+
{
|
|
31281
|
+
type: "range",
|
|
31282
|
+
min: "5",
|
|
31283
|
+
max: "30",
|
|
31284
|
+
step: "5",
|
|
31285
|
+
value: state.percentile,
|
|
31286
|
+
onChange: (e) => handlePercentileChange(parseInt(e.target.value)),
|
|
31287
|
+
className: "w-24 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
|
|
31288
|
+
}
|
|
31289
|
+
),
|
|
31290
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold text-blue-600 min-w-[50px]", children: [
|
|
31291
|
+
state.percentile,
|
|
31292
|
+
"%"
|
|
31293
|
+
] })
|
|
31294
|
+
] }),
|
|
31295
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [5, 10, 15, 20].map((preset) => /* @__PURE__ */ jsxs(
|
|
31296
|
+
"button",
|
|
31297
|
+
{
|
|
31298
|
+
onClick: () => handlePercentileChange(preset),
|
|
31299
|
+
className: `px-2 py-1 text-xs font-medium rounded transition-colors ${state.percentile === preset ? "bg-blue-600 text-white" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
|
|
31300
|
+
children: [
|
|
31301
|
+
preset,
|
|
31302
|
+
"%"
|
|
31303
|
+
]
|
|
31304
|
+
},
|
|
31305
|
+
preset
|
|
31306
|
+
)) })
|
|
31307
|
+
] }),
|
|
31308
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
31309
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-3", children: [
|
|
31310
|
+
/* @__PURE__ */ jsx(Layers, { className: "h-4 w-4 text-purple-600" }),
|
|
31311
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-gray-700", children: "Types:" }),
|
|
31312
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
31313
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-1 cursor-pointer", children: [
|
|
31314
|
+
/* @__PURE__ */ jsx(
|
|
31315
|
+
"input",
|
|
31316
|
+
{
|
|
31317
|
+
type: "checkbox",
|
|
31318
|
+
checked: state.showFastCycles,
|
|
31319
|
+
onChange: () => handleClipTypeToggle("showFastCycles"),
|
|
31320
|
+
className: "w-3 h-3 text-blue-600 border-gray-300 rounded"
|
|
31321
|
+
}
|
|
31322
|
+
),
|
|
31323
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-700", children: "Fast" })
|
|
31324
|
+
] }),
|
|
31325
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-1 cursor-pointer", children: [
|
|
31326
|
+
/* @__PURE__ */ jsx(
|
|
31327
|
+
"input",
|
|
31328
|
+
{
|
|
31329
|
+
type: "checkbox",
|
|
31330
|
+
checked: state.showSlowCycles,
|
|
31331
|
+
onChange: () => handleClipTypeToggle("showSlowCycles"),
|
|
31332
|
+
className: "w-3 h-3 text-blue-600 border-gray-300 rounded"
|
|
31333
|
+
}
|
|
31334
|
+
),
|
|
31335
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-700", children: "Slow" })
|
|
31336
|
+
] }),
|
|
31337
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-1 cursor-pointer", children: [
|
|
31338
|
+
/* @__PURE__ */ jsx(
|
|
31339
|
+
"input",
|
|
31340
|
+
{
|
|
31341
|
+
type: "checkbox",
|
|
31342
|
+
checked: state.showLongestIdles,
|
|
31343
|
+
onChange: () => handleClipTypeToggle("showLongestIdles"),
|
|
31344
|
+
className: "w-3 h-3 text-blue-600 border-gray-300 rounded"
|
|
31345
|
+
}
|
|
31346
|
+
),
|
|
31347
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-700", children: "LongIdle" })
|
|
31348
|
+
] }),
|
|
31349
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-1 cursor-pointer", children: [
|
|
31350
|
+
/* @__PURE__ */ jsx(
|
|
31351
|
+
"input",
|
|
31352
|
+
{
|
|
31353
|
+
type: "checkbox",
|
|
31354
|
+
checked: state.showCycleCompletion,
|
|
31355
|
+
onChange: () => handleClipTypeToggle("showCycleCompletion"),
|
|
31356
|
+
className: "w-3 h-3 text-blue-600 border-gray-300 rounded"
|
|
31357
|
+
}
|
|
31358
|
+
),
|
|
31359
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-700", children: "Completion" })
|
|
31360
|
+
] }),
|
|
31361
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center space-x-1 cursor-pointer", children: [
|
|
31362
|
+
/* @__PURE__ */ jsx(
|
|
31363
|
+
"input",
|
|
31364
|
+
{
|
|
31365
|
+
type: "checkbox",
|
|
31366
|
+
checked: state.showIdleTime,
|
|
31367
|
+
onChange: () => handleClipTypeToggle("showIdleTime"),
|
|
31368
|
+
className: "w-3 h-3 text-blue-600 border-gray-300 rounded"
|
|
31369
|
+
}
|
|
31370
|
+
),
|
|
31371
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-gray-700", children: "Idle" })
|
|
31372
|
+
] })
|
|
31373
|
+
] })
|
|
31374
|
+
] }),
|
|
31375
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
31376
|
+
/* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4 text-indigo-600" }),
|
|
31377
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-gray-700", children: "Shift:" }),
|
|
31378
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
|
|
31379
|
+
/* @__PURE__ */ jsx(
|
|
31380
|
+
"button",
|
|
31381
|
+
{
|
|
31382
|
+
onClick: () => handleShiftChange(void 0),
|
|
31383
|
+
className: `px-2 py-1 text-xs font-medium rounded transition-colors ${state.shiftFilter === void 0 ? "bg-indigo-600 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
|
|
31384
|
+
children: "All"
|
|
31385
|
+
}
|
|
31386
|
+
),
|
|
31387
|
+
/* @__PURE__ */ jsx(
|
|
31388
|
+
"button",
|
|
31389
|
+
{
|
|
31390
|
+
onClick: () => handleShiftChange(0),
|
|
31391
|
+
className: `px-2 py-1 text-xs font-medium rounded transition-colors ${state.shiftFilter === 0 ? "bg-indigo-600 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
|
|
31392
|
+
children: "Day"
|
|
31393
|
+
}
|
|
31394
|
+
),
|
|
31395
|
+
/* @__PURE__ */ jsx(
|
|
31396
|
+
"button",
|
|
31397
|
+
{
|
|
31398
|
+
onClick: () => handleShiftChange(1),
|
|
31399
|
+
className: `px-2 py-1 text-xs font-medium rounded transition-colors ${state.shiftFilter === 1 ? "bg-indigo-600 text-white" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
|
|
31400
|
+
children: "Night"
|
|
31401
|
+
}
|
|
31402
|
+
)
|
|
31403
|
+
] })
|
|
31404
|
+
] })
|
|
31405
|
+
] }),
|
|
31406
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between pt-2 border-t border-gray-100", children: [
|
|
31407
|
+
/* @__PURE__ */ jsxs(
|
|
31408
|
+
"button",
|
|
31409
|
+
{
|
|
31410
|
+
onClick: handleReset,
|
|
31411
|
+
className: "flex items-center space-x-1 px-3 py-1.5 text-xs font-medium text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors",
|
|
31412
|
+
children: [
|
|
31413
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "h-3 w-3" }),
|
|
31414
|
+
/* @__PURE__ */ jsx("span", { children: "Reset" })
|
|
31415
|
+
]
|
|
31416
|
+
}
|
|
31417
|
+
),
|
|
31418
|
+
/* @__PURE__ */ jsxs(
|
|
31419
|
+
"button",
|
|
31420
|
+
{
|
|
31421
|
+
onClick: () => {
|
|
31422
|
+
onApply?.();
|
|
31423
|
+
toggleAdvancedPanel();
|
|
31424
|
+
},
|
|
31425
|
+
className: "flex items-center space-x-1 px-3 py-1.5 text-xs font-medium text-white bg-blue-600 rounded-lg hover:bg-blue-700 transition-colors",
|
|
31426
|
+
children: [
|
|
31427
|
+
/* @__PURE__ */ jsx(Filter, { className: "h-3 w-3" }),
|
|
31428
|
+
/* @__PURE__ */ jsx("span", { children: "Apply & Close" })
|
|
31429
|
+
]
|
|
31430
|
+
}
|
|
31431
|
+
)
|
|
31432
|
+
] })
|
|
31433
|
+
] }),
|
|
31434
|
+
!state.isAdvancedPanelOpen && /* @__PURE__ */ jsx("div", { className: "px-4 py-3", children: /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-4 text-sm", children: [
|
|
31435
|
+
/* @__PURE__ */ jsxs("span", { className: "text-gray-600", children: [
|
|
31436
|
+
"Percentile: ",
|
|
31437
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold text-blue-600", children: [
|
|
31438
|
+
state.percentile,
|
|
31439
|
+
"%"
|
|
31440
|
+
] })
|
|
31441
|
+
] }),
|
|
31442
|
+
/* @__PURE__ */ jsxs("span", { className: "text-gray-600", children: [
|
|
31443
|
+
"Shift: ",
|
|
31444
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-indigo-600", children: state.shiftFilter === void 0 ? "All" : state.shiftFilter === 0 ? "Day" : "Night" })
|
|
31445
|
+
] }),
|
|
31446
|
+
/* @__PURE__ */ jsxs("span", { className: "text-gray-600", children: [
|
|
31447
|
+
"Categories: ",
|
|
31448
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-purple-600", children: [
|
|
31449
|
+
state.showFastCycles && "Fast",
|
|
31450
|
+
state.showSlowCycles && "Slow",
|
|
31451
|
+
state.showLongestIdles && "Idle",
|
|
31452
|
+
state.showCycleCompletion && "Completion"
|
|
31453
|
+
].filter(Boolean).join(", ") || "None" })
|
|
31454
|
+
] })
|
|
31455
|
+
] }) }) })
|
|
31456
|
+
] });
|
|
31457
|
+
};
|
|
31245
31458
|
var HamburgerButton = ({
|
|
31246
31459
|
onClick,
|
|
31247
31460
|
className = "",
|
|
@@ -31274,7 +31487,7 @@ var Breadcrumbs = ({ items }) => {
|
|
|
31274
31487
|
}
|
|
31275
31488
|
}
|
|
31276
31489
|
};
|
|
31277
|
-
return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-1 flex items-center space-x-1 text-xs font-medium text-gray-500 dark:text-gray-400", children: items.map((item, index) => /* @__PURE__ */ jsxs(
|
|
31490
|
+
return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "mb-1 flex items-center space-x-1 text-xs font-medium text-gray-500 dark:text-gray-400", children: items.map((item, index) => /* @__PURE__ */ jsxs(React20__default.Fragment, { children: [
|
|
31278
31491
|
index > 0 && /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 text-gray-400 dark:text-gray-500" }),
|
|
31279
31492
|
/* @__PURE__ */ jsxs(
|
|
31280
31493
|
"span",
|
|
@@ -33731,7 +33944,7 @@ var ThreadSidebar = ({
|
|
|
33731
33944
|
] });
|
|
33732
33945
|
};
|
|
33733
33946
|
var axelProfilePng = "/axel-profile.png";
|
|
33734
|
-
var ProfilePicture =
|
|
33947
|
+
var ProfilePicture = React20__default.memo(({ alt = "Axel", className = "w-8 h-8 sm:w-10 sm:h-10 md:w-12 md:h-12" }) => {
|
|
33735
33948
|
return /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: `${className} rounded-xl overflow-hidden shadow-sm`, children: /* @__PURE__ */ jsx(
|
|
33736
33949
|
"img",
|
|
33737
33950
|
{
|
|
@@ -34622,7 +34835,7 @@ var AIAgentView = () => {
|
|
|
34622
34835
|
barRadius: [4, 4, 0, 0]
|
|
34623
34836
|
// Top corners rounded
|
|
34624
34837
|
};
|
|
34625
|
-
const
|
|
34838
|
+
const CustomTooltip2 = ({ active, payload, label }) => {
|
|
34626
34839
|
if (active && payload && payload.length) {
|
|
34627
34840
|
return /* @__PURE__ */ jsxs("div", { className: "bg-white px-4 py-3 shadow-lg rounded-lg border border-gray-200", children: [
|
|
34628
34841
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 mb-1", children: label }),
|
|
@@ -34722,7 +34935,7 @@ var AIAgentView = () => {
|
|
|
34722
34935
|
tickFormatter: (value) => formatNumber(value)
|
|
34723
34936
|
}
|
|
34724
34937
|
),
|
|
34725
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(
|
|
34938
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip2, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
|
|
34726
34939
|
/* @__PURE__ */ jsx(
|
|
34727
34940
|
Bar,
|
|
34728
34941
|
{
|
|
@@ -34766,7 +34979,7 @@ var AIAgentView = () => {
|
|
|
34766
34979
|
tickFormatter: (value) => formatNumber(value)
|
|
34767
34980
|
}
|
|
34768
34981
|
),
|
|
34769
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(
|
|
34982
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip2, {}), cursor: { strokeDasharray: "3 3" } }),
|
|
34770
34983
|
/* @__PURE__ */ jsx(
|
|
34771
34984
|
Line,
|
|
34772
34985
|
{
|
|
@@ -34886,7 +35099,7 @@ var AIAgentView = () => {
|
|
|
34886
35099
|
tickFormatter: (value) => formatNumber(value)
|
|
34887
35100
|
}
|
|
34888
35101
|
),
|
|
34889
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(
|
|
35102
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip2, {}), cursor: { strokeDasharray: "3 3" } }),
|
|
34890
35103
|
/* @__PURE__ */ jsx(
|
|
34891
35104
|
Legend,
|
|
34892
35105
|
{
|
|
@@ -34946,7 +35159,7 @@ var AIAgentView = () => {
|
|
|
34946
35159
|
tickFormatter: (value) => formatNumber(value)
|
|
34947
35160
|
}
|
|
34948
35161
|
),
|
|
34949
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(
|
|
35162
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip2, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
|
|
34950
35163
|
/* @__PURE__ */ jsx(
|
|
34951
35164
|
Legend,
|
|
34952
35165
|
{
|
|
@@ -35110,7 +35323,7 @@ var AIAgentView = () => {
|
|
|
35110
35323
|
Tooltip,
|
|
35111
35324
|
{
|
|
35112
35325
|
cursor: { strokeDasharray: "3 3" },
|
|
35113
|
-
content: /* @__PURE__ */ jsx(
|
|
35326
|
+
content: /* @__PURE__ */ jsx(CustomTooltip2, {})
|
|
35114
35327
|
}
|
|
35115
35328
|
),
|
|
35116
35329
|
/* @__PURE__ */ jsx(
|
|
@@ -35180,7 +35393,7 @@ var AIAgentView = () => {
|
|
|
35180
35393
|
tickFormatter: (value) => formatNumber(value)
|
|
35181
35394
|
}
|
|
35182
35395
|
),
|
|
35183
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(
|
|
35396
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip2, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
|
|
35184
35397
|
/* @__PURE__ */ jsx(
|
|
35185
35398
|
Legend,
|
|
35186
35399
|
{
|
|
@@ -35254,7 +35467,7 @@ var AIAgentView = () => {
|
|
|
35254
35467
|
tickFormatter: (value) => formatNumber(value)
|
|
35255
35468
|
}
|
|
35256
35469
|
),
|
|
35257
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(
|
|
35470
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(CustomTooltip2, {}), cursor: { strokeDasharray: "3 3" } }),
|
|
35258
35471
|
/* @__PURE__ */ jsx(
|
|
35259
35472
|
Legend,
|
|
35260
35473
|
{
|
|
@@ -36440,7 +36653,7 @@ function HomeView({
|
|
|
36440
36653
|
animate: { opacity: 1, scale: 1 },
|
|
36441
36654
|
transition: { duration: 0.3 },
|
|
36442
36655
|
className: "h-full",
|
|
36443
|
-
children:
|
|
36656
|
+
children: React20__default.createElement(WorkspaceGrid, {
|
|
36444
36657
|
workspaces: memoizedWorkspaceMetrics,
|
|
36445
36658
|
lineNames,
|
|
36446
36659
|
factoryView: factoryViewId,
|
|
@@ -36466,7 +36679,7 @@ function HomeView({
|
|
|
36466
36679
|
animate: { opacity: 1, scale: 1 },
|
|
36467
36680
|
transition: { duration: 0.3 },
|
|
36468
36681
|
className: "h-full",
|
|
36469
|
-
children:
|
|
36682
|
+
children: React20__default.createElement(WorkspaceGrid, {
|
|
36470
36683
|
workspaces: [],
|
|
36471
36684
|
// Show empty grid while loading
|
|
36472
36685
|
lineNames,
|
|
@@ -36493,7 +36706,7 @@ function HomeView({
|
|
|
36493
36706
|
}
|
|
36494
36707
|
);
|
|
36495
36708
|
}
|
|
36496
|
-
var AuthenticatedHomeView = withAuth(
|
|
36709
|
+
var AuthenticatedHomeView = withAuth(React20__default.memo(HomeView));
|
|
36497
36710
|
var HomeView_default = HomeView;
|
|
36498
36711
|
function withWorkspaceDisplayNames(Component3, options = {}) {
|
|
36499
36712
|
const {
|
|
@@ -37611,7 +37824,7 @@ var KPIDetailViewWithDisplayNames = withSelectedLineDisplayNames(KPIDetailView);
|
|
|
37611
37824
|
var KPIDetailView_default = KPIDetailViewWithDisplayNames;
|
|
37612
37825
|
var LineCard = ({ line, onClick, supervisorEnabled = false }) => {
|
|
37613
37826
|
const { kpis, isLoading, error } = useLineKPIs({ lineId: line.id });
|
|
37614
|
-
const isOnTrack =
|
|
37827
|
+
const isOnTrack = React20__default.useMemo(() => {
|
|
37615
37828
|
if (!kpis) return null;
|
|
37616
37829
|
return kpis.efficiency.value > 90;
|
|
37617
37830
|
}, [kpis]);
|
|
@@ -39414,7 +39627,7 @@ var ShiftsView = ({
|
|
|
39414
39627
|
] })
|
|
39415
39628
|
] });
|
|
39416
39629
|
};
|
|
39417
|
-
var AuthenticatedShiftsView = withAuth(
|
|
39630
|
+
var AuthenticatedShiftsView = withAuth(React20__default.memo(ShiftsView));
|
|
39418
39631
|
var ShiftsView_default = ShiftsView;
|
|
39419
39632
|
|
|
39420
39633
|
// src/lib/constants/actions.ts
|
|
@@ -41330,7 +41543,7 @@ var TargetsView = ({
|
|
|
41330
41543
|
};
|
|
41331
41544
|
var TargetsViewWithDisplayNames = withAllWorkspaceDisplayNames(TargetsView);
|
|
41332
41545
|
var TargetsView_default = TargetsViewWithDisplayNames;
|
|
41333
|
-
var AuthenticatedTargetsView = withAuth(
|
|
41546
|
+
var AuthenticatedTargetsView = withAuth(React20__default.memo(TargetsViewWithDisplayNames));
|
|
41334
41547
|
|
|
41335
41548
|
// src/views/workspace-detail-view.utils.ts
|
|
41336
41549
|
var formatISTDate2 = (date = /* @__PURE__ */ new Date(), options) => {
|
|
@@ -42219,7 +42432,7 @@ var WorkspaceDetailView = ({
|
|
|
42219
42432
|
}
|
|
42220
42433
|
)
|
|
42221
42434
|
] }),
|
|
42222
|
-
activeTab === "bottlenecks" && /* @__PURE__ */ jsx(
|
|
42435
|
+
activeTab === "bottlenecks" && /* @__PURE__ */ jsx(ClipFilterProvider, { children: /* @__PURE__ */ jsx(
|
|
42223
42436
|
BottlenecksContent,
|
|
42224
42437
|
{
|
|
42225
42438
|
workspaceId,
|
|
@@ -42228,7 +42441,7 @@ var WorkspaceDetailView = ({
|
|
|
42228
42441
|
shift,
|
|
42229
42442
|
className: "h-[calc(100vh-10rem)]"
|
|
42230
42443
|
}
|
|
42231
|
-
)
|
|
42444
|
+
) })
|
|
42232
42445
|
] })
|
|
42233
42446
|
] }),
|
|
42234
42447
|
/* @__PURE__ */ jsx(
|
|
@@ -43304,4 +43517,175 @@ var streamProxyConfig = {
|
|
|
43304
43517
|
}
|
|
43305
43518
|
};
|
|
43306
43519
|
|
|
43307
|
-
|
|
43520
|
+
// src/lib/api/s3-clips-parser.ts
|
|
43521
|
+
function parseS3Uri(s3Uri, sopCategories) {
|
|
43522
|
+
const path = new URL(s3Uri).pathname;
|
|
43523
|
+
const parts = path.split("/").filter((p) => p);
|
|
43524
|
+
if (s3Uri.includes("missed_qchecks")) {
|
|
43525
|
+
console.warn(`Skipping missed_qchecks URI in parseS3Uri: ${s3Uri}`);
|
|
43526
|
+
return null;
|
|
43527
|
+
}
|
|
43528
|
+
if (parts.length < 8) {
|
|
43529
|
+
console.warn(`Invalid S3 path structure: ${s3Uri} - Too few parts: ${parts.length}, expected at least 8`);
|
|
43530
|
+
return null;
|
|
43531
|
+
}
|
|
43532
|
+
try {
|
|
43533
|
+
const datePart = parts[2];
|
|
43534
|
+
const shiftPart = parts[3];
|
|
43535
|
+
const violationType = parts[4];
|
|
43536
|
+
let folderName = "";
|
|
43537
|
+
let timestamp = "";
|
|
43538
|
+
for (let i = 5; i < parts.length; i++) {
|
|
43539
|
+
const part = parts[i];
|
|
43540
|
+
if (part && part.includes("_") && /\d{8}_\d{6}/.test(part)) {
|
|
43541
|
+
folderName = part;
|
|
43542
|
+
const timeMatch = folderName.match(/_(\d{8})_(\d{6})_/);
|
|
43543
|
+
if (timeMatch) {
|
|
43544
|
+
timestamp = `${timeMatch[2].substring(0, 2)}:${timeMatch[2].substring(2, 4)}:${timeMatch[2].substring(4, 6)}`;
|
|
43545
|
+
break;
|
|
43546
|
+
}
|
|
43547
|
+
}
|
|
43548
|
+
}
|
|
43549
|
+
if (!timestamp) {
|
|
43550
|
+
console.warn(`Couldn't extract timestamp from any part: ${parts.join("/")}`);
|
|
43551
|
+
timestamp = "00:00:00";
|
|
43552
|
+
}
|
|
43553
|
+
let severity = "low";
|
|
43554
|
+
let type = "bottleneck";
|
|
43555
|
+
let description = "Analysis Clip";
|
|
43556
|
+
const normalizedViolationType = violationType.toLowerCase().trim();
|
|
43557
|
+
if (sopCategories && sopCategories.length > 0) {
|
|
43558
|
+
const matchedCategory = sopCategories.find((category) => {
|
|
43559
|
+
const categoryId = category.id.toLowerCase();
|
|
43560
|
+
const s3FolderName = (category.s3FolderName || category.id).toLowerCase();
|
|
43561
|
+
return categoryId === normalizedViolationType || s3FolderName === normalizedViolationType || // Also check for partial matches for flexibility
|
|
43562
|
+
normalizedViolationType.includes(categoryId) || normalizedViolationType.includes(s3FolderName);
|
|
43563
|
+
});
|
|
43564
|
+
if (matchedCategory) {
|
|
43565
|
+
type = matchedCategory.id;
|
|
43566
|
+
description = matchedCategory.description || matchedCategory.label;
|
|
43567
|
+
if (matchedCategory.color.includes("red")) {
|
|
43568
|
+
severity = "high";
|
|
43569
|
+
} else if (matchedCategory.color.includes("yellow") || matchedCategory.color.includes("orange")) {
|
|
43570
|
+
severity = "medium";
|
|
43571
|
+
} else {
|
|
43572
|
+
severity = "low";
|
|
43573
|
+
}
|
|
43574
|
+
console.log(`Matched SOP category: ${matchedCategory.id} for violation type: ${violationType}`);
|
|
43575
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
43576
|
+
}
|
|
43577
|
+
}
|
|
43578
|
+
switch (normalizedViolationType) {
|
|
43579
|
+
case "idle_time":
|
|
43580
|
+
case "idle":
|
|
43581
|
+
case "low_value":
|
|
43582
|
+
case "low value":
|
|
43583
|
+
case "low_value_moment":
|
|
43584
|
+
case "low_value_moments":
|
|
43585
|
+
case "low value moment":
|
|
43586
|
+
case "low value moments":
|
|
43587
|
+
type = "low_value";
|
|
43588
|
+
severity = "low";
|
|
43589
|
+
description = "Idle Time Detected";
|
|
43590
|
+
break;
|
|
43591
|
+
case "sop_deviation":
|
|
43592
|
+
type = "missing_quality_check";
|
|
43593
|
+
severity = "high";
|
|
43594
|
+
description = "SOP Deviations";
|
|
43595
|
+
break;
|
|
43596
|
+
case "long_cycle_time":
|
|
43597
|
+
severity = "high";
|
|
43598
|
+
type = "long_cycle_time";
|
|
43599
|
+
description = "Long Cycle Time Detected";
|
|
43600
|
+
break;
|
|
43601
|
+
case "best_cycle_time":
|
|
43602
|
+
type = "best_cycle_time";
|
|
43603
|
+
severity = "low";
|
|
43604
|
+
description = "Best Cycle Time Performance";
|
|
43605
|
+
break;
|
|
43606
|
+
case "worst_cycle_time":
|
|
43607
|
+
type = "worst_cycle_time";
|
|
43608
|
+
severity = "high";
|
|
43609
|
+
description = "Worst Cycle Time Performance";
|
|
43610
|
+
break;
|
|
43611
|
+
case "cycle_completion":
|
|
43612
|
+
case "completed_cycles":
|
|
43613
|
+
case "completed_cycle":
|
|
43614
|
+
type = "cycle_completion";
|
|
43615
|
+
severity = "low";
|
|
43616
|
+
description = "Cycle Completion";
|
|
43617
|
+
break;
|
|
43618
|
+
case "running_cycle":
|
|
43619
|
+
case "active_cycle":
|
|
43620
|
+
case "production_cycle":
|
|
43621
|
+
type = "running_cycle";
|
|
43622
|
+
severity = "low";
|
|
43623
|
+
description = "Active Production Cycle";
|
|
43624
|
+
break;
|
|
43625
|
+
case "setup_state":
|
|
43626
|
+
case "machine_setup":
|
|
43627
|
+
case "line_setup":
|
|
43628
|
+
type = "setup_state";
|
|
43629
|
+
severity = "medium";
|
|
43630
|
+
description = "Machine Setup Activity";
|
|
43631
|
+
break;
|
|
43632
|
+
case "medium_bottleneck":
|
|
43633
|
+
severity = "medium";
|
|
43634
|
+
description = "Medium Bottleneck Identified";
|
|
43635
|
+
break;
|
|
43636
|
+
case "minor_bottleneck":
|
|
43637
|
+
case "mild_bottleneck":
|
|
43638
|
+
severity = "low";
|
|
43639
|
+
description = "Minor Bottleneck Identified";
|
|
43640
|
+
break;
|
|
43641
|
+
default:
|
|
43642
|
+
if (normalizedViolationType.includes("sop") && normalizedViolationType.includes("deviation")) {
|
|
43643
|
+
type = "missing_quality_check";
|
|
43644
|
+
severity = "high";
|
|
43645
|
+
description = "SOP Deviations";
|
|
43646
|
+
} else if (normalizedViolationType.includes("worst") && normalizedViolationType.includes("cycle")) {
|
|
43647
|
+
type = "worst_cycle_time";
|
|
43648
|
+
severity = "high";
|
|
43649
|
+
description = "Worst Cycle Time Performance";
|
|
43650
|
+
} else if (normalizedViolationType.includes("best") && normalizedViolationType.includes("cycle")) {
|
|
43651
|
+
type = "best_cycle_time";
|
|
43652
|
+
severity = "low";
|
|
43653
|
+
description = "Best Cycle Time Performance";
|
|
43654
|
+
} else if (normalizedViolationType.includes("long") && normalizedViolationType.includes("cycle")) {
|
|
43655
|
+
type = "long_cycle_time";
|
|
43656
|
+
severity = "high";
|
|
43657
|
+
description = "Long Cycle Time Detected";
|
|
43658
|
+
} else if (normalizedViolationType.includes("cycle") && (normalizedViolationType.includes("completion") || normalizedViolationType.includes("complete"))) {
|
|
43659
|
+
type = "cycle_completion";
|
|
43660
|
+
severity = "low";
|
|
43661
|
+
description = "Cycle Completion";
|
|
43662
|
+
} else if (normalizedViolationType.includes("running") && normalizedViolationType.includes("cycle")) {
|
|
43663
|
+
type = "running_cycle";
|
|
43664
|
+
severity = "low";
|
|
43665
|
+
description = "Active Production Cycle";
|
|
43666
|
+
} else if (normalizedViolationType.includes("setup") || normalizedViolationType.includes("machine") && normalizedViolationType.includes("setup")) {
|
|
43667
|
+
type = "setup_state";
|
|
43668
|
+
severity = "medium";
|
|
43669
|
+
description = "Machine Setup Activity";
|
|
43670
|
+
} else {
|
|
43671
|
+
description = `Clip type: ${violationType.replace(/_/g, " ")}`;
|
|
43672
|
+
console.log(`Detected unknown violation type: ${violationType} in URI: ${s3Uri}`);
|
|
43673
|
+
}
|
|
43674
|
+
break;
|
|
43675
|
+
}
|
|
43676
|
+
return { timestamp, severity, description, type, originalUri: s3Uri };
|
|
43677
|
+
} catch (error) {
|
|
43678
|
+
console.error(`Error parsing S3 URI: ${s3Uri}`, error);
|
|
43679
|
+
return null;
|
|
43680
|
+
}
|
|
43681
|
+
}
|
|
43682
|
+
function shuffleArray(array) {
|
|
43683
|
+
const shuffled = [...array];
|
|
43684
|
+
for (let i = shuffled.length - 1; i > 0; i--) {
|
|
43685
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
43686
|
+
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
|
|
43687
|
+
}
|
|
43688
|
+
return shuffled;
|
|
43689
|
+
}
|
|
43690
|
+
|
|
43691
|
+
export { ACTION_NAMES, AIAgentView_default as AIAgentView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedWorkspaceHealthView, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_SHIFT_CONFIG, DEFAULT_THEME_CONFIG, DEFAULT_VIDEO_CONFIG, DEFAULT_WORKSPACE_CONFIG, DEFAULT_WORKSPACE_POSITIONS, DashboardHeader, DashboardLayout, DashboardOverridesProvider, DashboardProvider, DateDisplay_default as DateDisplay, DateTimeDisplay, DebugAuth, DebugAuthView_default as DebugAuthView, DetailedHealthStatus, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, KPICard, KPIDetailView_default as KPIDetailView, KPIGrid, KPIHeader, KPISection, KPIsOverviewView_default as KPIsOverviewView, LINE_1_UUID, LINE_2_UUID, LargeOutputProgressChart, LeaderboardDetailView_default as LeaderboardDetailView, Legend6 as Legend, LineChart, LineHistoryCalendar, LineMonthlyHistory, LineMonthlyPdfGenerator, LinePdfExportButton, LinePdfGenerator, LineWhatsAppShareButton, LinesService, LiveTimer, LoadingInline, LoadingOverlay_default as LoadingOverlay, LoadingPage_default as LoadingPage, LoadingSkeleton, LoadingState, LoginPage, LoginView_default as LoginView, MainLayout, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PrefetchConfigurationError, PrefetchError, PrefetchEvents, PrefetchStatus, PrefetchTimeoutError, ProfileView_default as ProfileView, RegistryProvider, S3ClipsSupabaseService as S3ClipsService, S3Service, SKUManagementView, SOPComplianceChart, SSEChatClient, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, ShiftDisplay_default as ShiftDisplay, ShiftsView_default as ShiftsView, SideNavBar, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TimeDisplay_default as TimeDisplay, TimePickerDropdown, UserService, VideoCard, VideoGridView, VideoPlayer, VideoPreloader, WORKSPACE_POSITIONS, WhatsAppShareButton, WorkspaceCard, WorkspaceDetailView_default as WorkspaceDetailView, WorkspaceDisplayNameExample, WorkspaceGrid, WorkspaceGridItem, WorkspaceHealthCard, WorkspaceHealthView_default as WorkspaceHealthView, WorkspaceHistoryCalendar, WorkspaceMetricCards, WorkspaceMetricCardsImpl, WorkspaceMonthlyDataFetcher, WorkspaceMonthlyHistory, WorkspaceMonthlyPdfGenerator, WorkspacePdfExportButton, WorkspacePdfGenerator, WorkspaceWhatsAppShareButton, actionService, apiUtils, authCoreService, authOTPService, authRateLimitService, checkRateLimit2 as checkRateLimit, clearAllRateLimits2 as clearAllRateLimits, clearRateLimit2 as clearRateLimit, clearS3VideoCache, clearS3VideoFromCache, clearWorkspaceDisplayNamesCache, cn, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isTransitionPeriod, isUrlPermanentlyFailed, isValidFactoryViewConfiguration, isValidLineInfoPayload, isValidPrefetchParams, isValidPrefetchStatus, isValidWorkspaceDetailedMetricsPayload, isValidWorkspaceMetricsPayload, isWorkspaceDisplayNamesLoaded, isWorkspaceDisplayNamesLoading, linesService, mergeWithDefaultConfig, migrateLegacyConfiguration, optifyeAgentClient, parseS3Uri, preInitializeWorkspaceDisplayNames, preloadS3Video, preloadS3VideoUrl, preloadS3VideosUrl, preloadVideoUrl, preloadVideosUrl, qualityService, realtimeService, refreshWorkspaceDisplayNames, resetCoreMixpanel, resetFailedUrl, resetSubscriptionManager, s3VideoPreloader, shuffleArray, simulateApiDelay, skuService, startCoreSessionRecording, stopCoreSessionRecording, storeWorkspaceMapping, streamProxyConfig, throttledReloadDashboard, toUrlFriendlyName, trackCoreEvent, trackCorePageView, updateThreadTitle, useAccessControl, useActiveBreaks, useAllWorkspaceMetrics, useAnalyticsConfig, useAudioService, useAuth, useAuthConfig, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTheme, useThemeConfig, useThreads, useTicketHistory, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealth, useWorkspaceHealthById, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, workspaceHealthService, workspaceService };
|