@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.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import * as React19 from 'react';
2
- import React19__default, { createContext, useRef, useCallback, useState, useMemo, useEffect, memo, forwardRef, useImperativeHandle, useContext, useLayoutEffect, useId, Children, isValidElement, useInsertionEffect, Fragment as Fragment$1, createElement, Component } from 'react';
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, Activity, Sun, Moon, MousePointer, ArrowRight, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, Package, TrendingUp, TrendingDown, Building2, FolderOpen, Folder, HelpCircle, Play, Settings, LifeBuoy, EyeOff, Eye, Zap, UserCircle } from 'lucide-react';
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 = React19.createContext(void 0);
271
+ var DashboardConfigContext = React20.createContext(void 0);
272
272
  var DashboardProvider = ({ config: userProvidedConfig, children }) => {
273
- const fullConfig = React19.useMemo(() => mergeWithDefaultConfig(userProvidedConfig), [userProvidedConfig]);
273
+ const fullConfig = React20.useMemo(() => mergeWithDefaultConfig(userProvidedConfig), [userProvidedConfig]);
274
274
  _setDashboardConfigInstance(fullConfig);
275
- React19.useEffect(() => {
275
+ React20.useEffect(() => {
276
276
  _setDashboardConfigInstance(fullConfig);
277
277
  }, [fullConfig]);
278
- React19.useEffect(() => {
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 = React19.useContext(DashboardConfigContext);
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("date, shift_id, efficiency, total_output, avg_cycle_time, ideal_output, avg_pph, pph_threshold, workspace_rank, idle_time").eq("workspace_id", workspaceUuid).gte("date", formattedStartDate).lte("date", formattedEndDate).order("date", { ascending: true }).order("shift_id", { ascending: true });
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.ideal_output || 0,
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 getAuthToken2();
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 response = await this.fetchWithAuth("neighbors", {
4753
- workspaceId,
4754
- date,
4755
- shift: shiftId.toString(),
4756
- category,
4757
- currentClipId,
4758
- sopCategories: sopCategories || this.config.s3Config?.sopCategories?.default
4759
- });
4760
- return response.neighbors || { previous: null, next: null };
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
- var S3ClipsService2 = S3ClipsSupabaseService ;
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 S3ClipsService2(dashboardConfig));
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 React19.Component {
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: React19.cloneElement(children, { ref }) });
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
- React19.useEffect(() => {
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
- React19__default.useEffect(() => {
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 = React19.memo(function WithAuthComponent2(props) {
19651
+ const WithAuthComponent = React20.memo(function WithAuthComponent2(props) {
20353
19652
  const { session, loading, error } = useAuth();
20354
19653
  const router = useRouter();
20355
- React19.useEffect(() => {
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
- React19.useEffect(() => {
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 = React19__default.useRef(null);
20737
- const [containerReady, setContainerReady] = React19__default.useState(false);
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
- React19__default.useEffect(() => {
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 = React19__default.memo(BarChartComponent, (prevProps, nextProps) => {
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 = React19__default.useRef(null);
20887
- const [containerReady, setContainerReady] = React19__default.useState(false);
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
- React19__default.useEffect(() => {
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 = React19__default.memo(LineChartComponent, (prevProps, nextProps) => {
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 = React19__default.memo(OutputProgressChartComponent);
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 = React19__default.memo(CycleTimeChartComponent, (prevProps, nextProps) => {
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 = React19__default.useRef(null);
21209
- const [containerReady, setContainerReady] = React19__default.useState(false);
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] = React19__default.useState(Array(DURATION).fill(0));
21221
- const prevDataRef = React19__default.useRef(Array(DURATION).fill(0));
21222
- const animationFrameRef = React19__default.useRef(null);
21223
- const animateToNewData = React19__default.useCallback((targetData) => {
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
- React19__default.useEffect(() => {
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
- React19__default.useEffect(() => {
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19.forwardRef(
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 = React19__default.useRef(null);
21651
- const [containerReady, setContainerReady] = React19__default.useState(false);
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 } = React19__default.useMemo(() => {
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] = React19__default.useState(
20994
+ const [animatedData, setAnimatedData] = React20__default.useState(
21696
20995
  () => Array(SHIFT_DURATION).fill(0)
21697
20996
  );
21698
- const prevDataRef = React19__default.useRef(Array(SHIFT_DURATION).fill(0));
21699
- const animationFrameRef = React19__default.useRef(null);
21700
- React19__default.useEffect(() => {
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] = React19__default.useState({
21008
+ const [idleBarState, setIdleBarState] = React20__default.useState({
21710
21009
  visible: showIdleTime,
21711
21010
  key: 0,
21712
21011
  shouldAnimate: false
21713
21012
  });
21714
- const prevShowIdleTimeRef = React19__default.useRef(showIdleTime);
21715
- const stateUpdateTimeoutRef = React19__default.useRef(null);
21716
- React19__default.useEffect(() => {
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 = React19__default.useCallback((targetData) => {
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
- React19__default.useEffect(() => {
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
- React19__default.useEffect(() => {
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 = React19__default.useCallback((hourIndex) => {
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 = React19__default.useCallback((hourIndex) => {
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 = React19__default.useMemo(() => {
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 = React19__default.useMemo(() => {
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 = React19__default.memo(HourlyOutputChartComponent, (prevProps, nextProps) => {
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 = React19__default.memo(({
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 = React19__default.memo(({
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
- const prefetchCacheRef = useRef({});
21880
+ useRef({});
22582
21881
  const handleWorkspaceClick = useCallback((workspace) => {
22583
21882
  const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
22584
- if (dashboardConfig?.s3Config && workspaceId) {
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 CustomTooltip = ({ active, payload }) => {
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(CustomTooltip, {}) }),
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 (React19__default.isValidElement(iconType)) {
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
- return { data: dailyData, maxOutput, avgIdealOutput };
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: "200px" }, children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(
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: 5, right: 5, bottom: 35, left: 5 },
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: 9, fill: "#9ca3af" },
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
- tick: { fontSize: 9, fill: "#9ca3af" },
26732
- domain: [0, "dataMax"],
26733
- width: 30
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
- contentStyle: {
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: "3 3",
26083
+ strokeDasharray: "5 5",
26756
26084
  strokeWidth: 2
26757
26085
  }
26758
26086
  ),
26759
- /* @__PURE__ */ jsx(Bar, { dataKey: "output", radius: [3, 3, 0, 0], children: chartData.data.map((entry, index) => /* @__PURE__ */ jsx(
26760
- Cell,
26761
- {
26762
- fill: entry.output === 0 ? "#f3f4f6" : entry.color,
26763
- style: {
26764
- filter: "brightness(1)",
26765
- transition: "filter 0.3s ease, transform 0.3s ease",
26766
- cursor: "pointer"
26767
- },
26768
- onMouseEnter: (e) => {
26769
- const target = e.target;
26770
- target.style.filter = "brightness(1.1)";
26771
- target.style.transform = "translateY(-4px)";
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
- onMouseLeave: (e) => {
26774
- const target = e.target;
26775
- target.style.filter = "brightness(1)";
26776
- target.style.transform = "translateY(0)";
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
- `cell-${index}`
26780
- )) })
26134
+ )
26135
+ ] })
26781
26136
  ]
26782
26137
  }
26783
26138
  ) }) }),
26784
- chartData.avgIdealOutput > 0 && /* @__PURE__ */ jsx("div", { className: "flex justify-center items-center gap-4 mt-2", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
26785
- /* @__PURE__ */ jsx("div", { className: "w-8 h-0.5 border-t border-dashed", style: { borderColor: "#E34329" } }),
26786
- /* @__PURE__ */ jsxs("span", { className: "text-[9px] text-gray-500", children: [
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 = React19.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
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 = React19.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
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 = React19__default.forwardRef(({
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
- React19__default.useImperativeHandle(ref, () => ({
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 getSupabaseClient3 = () => {
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 getAuthToken3 = async () => {
27948
+ var getAuthToken2 = async () => {
28593
27949
  try {
28594
- const supabase = getSupabaseClient3();
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 getAuthToken3();
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 getAuthToken4()}`
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 getAuthToken4 = async () => {
28157
+ const getAuthToken3 = async () => {
28757
28158
  try {
28758
- const { createClient: createClient5 } = await import('@supabase/supabase-js');
28759
- const supabase = createClient5(
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.children && node.children.length > 0;
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__ */ jsx("div", { className: "mr-3", children: /* @__PURE__ */ jsx(Folder, { className: "h-5 w-5 text-slate-700" }) }),
28914
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx("h2", { className: "text-lg font-bold text-slate-900 tracking-tight", children: "Clips Explorer" }) })
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%-14rem)] overflow-y-auto scrollbar-thin", children: [
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: USE_SUPABASE_CLIPS2,
29123
- // Only enable for Supabase implementation
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 S3ClipsService3(dashboardConfig);
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
- const video = await s3ClipsService.getClipById(clipId);
29397
- if (video && isMountedRef.current) {
29398
- console.log(`[BottlenecksContent] Processing loaded video by ID: ${video.id}`);
29399
- setCurrentClipId(clipId);
29400
- setAllVideos([video]);
29401
- setCurrentIndex(0);
29402
- const counts = await s3ClipsService.getClipCounts(
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
- setCategoryPosition(1);
29408
- setIsNavigating(false);
29409
- console.log(`[BottlenecksContent] Successfully loaded clip by ID: ${video.id}`);
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 || !currentClipId || !s3ClipsService) return;
29368
+ if (!isMountedRef.current) return;
29445
29369
  const currentFilter = activeFilterRef.current;
29446
- console.log(`[handleNext] Navigating from clip ID: ${currentClipId}`);
29370
+ console.log(`[handleNext] Navigating in category: ${currentFilter}, current index: ${currentIndex}`);
29447
29371
  setIsNavigating(true);
29448
29372
  setError(null);
29449
29373
  try {
29450
- const neighbors = await s3ClipsService.getNeighboringClips(
29451
- workspaceId,
29452
- date || getOperationalDate(),
29453
- effectiveShift,
29454
- currentFilter,
29455
- currentClipId
29456
- );
29457
- if (neighbors.next) {
29458
- console.log(`[handleNext] Found next clip: ${neighbors.next.id}`);
29459
- setCurrentClipId(neighbors.next.id || null);
29460
- setAllVideos([neighbors.next]);
29461
- setCurrentIndex(0);
29462
- setCategoryPosition(categoryPosition + 1);
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
- console.log(`[handleNext] No next clip available`);
29467
- setIsNavigating(false);
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 getting next clip:`, error2);
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 || !currentClipId || !s3ClipsService) return;
29419
+ if (!isMountedRef.current) return;
29477
29420
  const currentFilter = activeFilterRef.current;
29478
- console.log(`[handlePrevious] Navigating from clip ID: ${currentClipId}`);
29421
+ console.log(`[handlePrevious] Navigating in category: ${currentFilter}, current index: ${currentIndex}`);
29479
29422
  setIsNavigating(true);
29480
29423
  setError(null);
29481
29424
  try {
29482
- const neighbors = await s3ClipsService.getNeighboringClips(
29483
- workspaceId,
29484
- date || getOperationalDate(),
29485
- effectiveShift,
29486
- currentFilter,
29487
- currentClipId
29488
- );
29489
- if (neighbors.previous) {
29490
- console.log(`[handlePrevious] Found previous clip: ${neighbors.previous.id}`);
29491
- setCurrentClipId(neighbors.previous.id || null);
29492
- setAllVideos([neighbors.previous]);
29493
- setCurrentIndex(0);
29494
- setCategoryPosition(Math.max(1, categoryPosition - 1));
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
- console.log(`[handlePrevious] No previous clip available`);
29499
- setIsNavigating(false);
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 getting previous clip:`, error2);
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 || (mergedCounts[activeFilter] || 0) === 0,
29694
- className: `p-2 rounded-full transition-colors ${categoryPosition <= 1 || (mergedCounts[activeFilter] || 0) === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"}`,
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: (mergedCounts[activeFilter] || 0) > 0 ? `${categoryPosition} / ${mergedCounts[activeFilter]}` : "0 / 0" }),
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 >= (mergedCounts[activeFilter] || 0) || (mergedCounts[activeFilter] || 0) === 0,
29708
- className: `p-2 rounded-full transition-colors ${categoryPosition >= (mergedCounts[activeFilter] || 0) || (mergedCounts[activeFilter] || 0) === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-100"}`,
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 === "best_cycle_time" || currentVideo.type === "worst_cycle_time" || currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || 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: [
29780
- /* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" ? "bg-purple-400" : currentVideo.type === "best_cycle_time" ? "bg-green-600" : currentVideo.type === "worst_cycle_time" ? "bg-red-700" : currentVideo.type === "cycle_completion" ? "bg-blue-600" : "bg-red-500"} mr-2 animate-pulse` }),
29781
- (currentVideo.type === "best_cycle_time" || currentVideo.type === "worst_cycle_time" || 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: [
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 = React19__default.memo(({
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 = React19__default.memo(({
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 = React19__default.useMemo(() => {
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 = React19__default.useMemo(() => {
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 = React19__default.useMemo(() => {
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(React19__default.Fragment, { children: [
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 = React19__default.memo(({ alt = "Axel", className = "w-8 h-8 sm:w-10 sm:h-10 md:w-12 md:h-12" }) => {
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 CustomTooltip = ({ active, payload, label }) => {
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(CustomTooltip, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
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(CustomTooltip, {}), cursor: { strokeDasharray: "3 3" } }),
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(CustomTooltip, {}), cursor: { strokeDasharray: "3 3" } }),
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(CustomTooltip, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
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(CustomTooltip, {})
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(CustomTooltip, {}), cursor: { fill: "rgba(0, 0, 0, 0.05)" } }),
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(CustomTooltip, {}), cursor: { strokeDasharray: "3 3" } }),
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: React19__default.createElement(WorkspaceGrid, {
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: React19__default.createElement(WorkspaceGrid, {
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(React19__default.memo(HomeView));
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 = React19__default.useMemo(() => {
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(React19__default.memo(ShiftsView));
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(React19__default.memo(TargetsViewWithDisplayNames));
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
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, 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, 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, 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, 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, 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 };
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 };