@optifye/dashboard-core 6.9.8 → 6.9.11

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
@@ -7,17 +7,15 @@ import { subDays, format, parseISO, isValid, formatDistanceToNow, isFuture, isTo
7
7
  import mixpanel from 'mixpanel-browser';
8
8
  import { EventEmitter } from 'events';
9
9
  import { createClient, REALTIME_SUBSCRIBE_STATES } from '@supabase/supabase-js';
10
- import Hls2 from 'hls.js';
10
+ import Hls3, { Events, ErrorTypes } from 'hls.js';
11
11
  import useSWR from 'swr';
12
12
  import { noop, warning, invariant, progress, secondsToMilliseconds, millisecondsToSeconds, memo as memo$1 } from 'motion-utils';
13
13
  import { getValueTransition, hover, press, isPrimaryPointer, GroupPlaybackControls, setDragLock, supportsLinearEasing, attachTimeline, isGenerator, calcGeneratorDuration, isWaapiSupportedEasing, mapEasingToNativeEasing, maxGeneratorDuration, generateLinearEasing, isBezierDefinition } from 'motion-dom';
14
- import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, XCircle, Maximize2, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, HelpCircle, Sliders, Activity, Layers, Filter, Search, Edit2, AlertTriangle, CheckCircle, Building2, Mail, Users, User, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, Package, UserPlus, Settings, LifeBuoy, EyeOff, Eye, MoreVertical, UserCog, Zap, Shield, UserCircle } from 'lucide-react';
14
+ import { Camera, ChevronDown, ChevronUp, Check, Map as Map$1, Video, ShieldCheck, Star, Award, ArrowLeft, X, Coffee, Plus, Clock, Calendar, Save, AlertCircle, Loader2, Minus, ArrowDown, ArrowUp, ChevronLeft, ChevronRight, Pause, Play, XCircle, Sparkles, TrendingUp, Settings2, CheckCircle2, RefreshCw, TrendingDown, FolderOpen, Folder, HelpCircle, Sliders, Activity, Layers, Filter, Search, Edit2, AlertTriangle, CheckCircle, Building2, Mail, Users, User, Lock, ArrowRight, Info, Share2, Trophy, Target, Download, Sun, Moon, MousePointer, MessageSquare, Trash2, Menu, Send, Copy, UserCheck, LogOut, Package, UserPlus, Settings, LifeBuoy, EyeOff, Eye, MoreVertical, UserCog, Zap, Shield, UserCircle } from 'lucide-react';
15
15
  import { toast } from 'sonner';
16
16
  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';
17
17
  import { Slot } from '@radix-ui/react-slot';
18
18
  import * as SelectPrimitive from '@radix-ui/react-select';
19
- import videojs from 'video.js';
20
- import 'video.js/dist/video-js.css';
21
19
  import { DayPicker, useNavigation as useNavigation$1 } from 'react-day-picker';
22
20
  import { XMarkIcon, ArrowRightIcon, HomeIcon, TrophyIcon, ChartBarIcon, AdjustmentsHorizontalIcon, ClockIcon, UsersIcon, TicketIcon, CubeIcon, SparklesIcon, QuestionMarkCircleIcon, HeartIcon, UserCircleIcon, ExclamationCircleIcon, EnvelopeIcon, DocumentTextIcon, ChevronUpIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, Bars3Icon, CheckCircleIcon, ChatBubbleLeftRightIcon, ArrowLeftIcon, XCircleIcon, InformationCircleIcon } from '@heroicons/react/24/outline';
23
21
  import { CheckIcon } from '@heroicons/react/24/solid';
@@ -8989,12 +8987,12 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
8989
8987
  const video = videoRef.current;
8990
8988
  if (!video) return;
8991
8989
  isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
8992
- if (Hls2.isSupported() && !isNativeHlsRef.current) {
8993
- const hls = new Hls2(HLS_CONFIG);
8990
+ if (Hls3.isSupported() && !isNativeHlsRef.current) {
8991
+ const hls = new Hls3(HLS_CONFIG);
8994
8992
  hlsRef.current = hls;
8995
8993
  hls.attachMedia(video);
8996
8994
  hls.loadSource(src);
8997
- hls.on(Hls2.Events.ERROR, (_, data) => {
8995
+ hls.on(Hls3.Events.ERROR, (_, data) => {
8998
8996
  if (!data.fatal) return;
8999
8997
  console.error("[HLS] Fatal error:", data.type, data.details);
9000
8998
  if (data.response?.code === 404) {
@@ -9002,8 +9000,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
9002
9000
  return;
9003
9001
  }
9004
9002
  switch (data.type) {
9005
- case Hls2.ErrorTypes.NETWORK_ERROR:
9006
- case Hls2.ErrorTypes.MEDIA_ERROR:
9003
+ case Hls3.ErrorTypes.NETWORK_ERROR:
9004
+ case Hls3.ErrorTypes.MEDIA_ERROR:
9007
9005
  softRestart(`${data.type}: ${data.details}`);
9008
9006
  break;
9009
9007
  default:
@@ -9011,7 +9009,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
9011
9009
  break;
9012
9010
  }
9013
9011
  });
9014
- hls.on(Hls2.Events.MANIFEST_PARSED, () => {
9012
+ hls.on(Hls3.Events.MANIFEST_PARSED, () => {
9015
9013
  if (failedUrls.has(src)) {
9016
9014
  console.log(`[HLS] Stream loaded successfully, resetting failure count for: ${src}`);
9017
9015
  failedUrls.delete(src);
@@ -14054,9 +14052,9 @@ var S3VideoPreloader = class {
14054
14052
  this.processQueue();
14055
14053
  };
14056
14054
  if (url.endsWith(".m3u8")) {
14057
- import('hls.js').then(({ default: Hls3 }) => {
14058
- if (Hls3.isSupported()) {
14059
- const hls = new Hls3({
14055
+ import('hls.js').then(({ default: Hls4 }) => {
14056
+ if (Hls4.isSupported()) {
14057
+ const hls = new Hls4({
14060
14058
  maxBufferLength: 10,
14061
14059
  startFragPrefetch: true,
14062
14060
  lowLatencyMode: false,
@@ -14068,7 +14066,7 @@ var S3VideoPreloader = class {
14068
14066
  hls.destroy();
14069
14067
  cleanup();
14070
14068
  }, 4e3);
14071
- hls.on(Hls3.Events.BUFFER_APPENDED, () => {
14069
+ hls.on(Hls4.Events.BUFFER_APPENDED, () => {
14072
14070
  window.clearTimeout(timeout);
14073
14071
  hls.destroy();
14074
14072
  cleanup();
@@ -23072,7 +23070,7 @@ var OutputProgressChartComponent = ({
23072
23070
  ];
23073
23071
  const COLORS = ["#00AB45", "#f3f4f6"];
23074
23072
  const percentage = (currentOutput / targetOutput * 100).toFixed(1);
23075
- return /* @__PURE__ */ jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-[200px] sm:max-w-[240px] lg:max-w-[280px] aspect-square", style: { minHeight: "120px", minWidth: "120px" }, children: [
23073
+ return /* @__PURE__ */ jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative w-full aspect-square max-h-full", style: { maxWidth: "min(100%, 280px)", containerType: "inline-size" }, children: [
23076
23074
  /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsx(PieChart, { children: /* @__PURE__ */ jsx(
23077
23075
  Pie,
23078
23076
  {
@@ -23096,16 +23094,33 @@ var OutputProgressChartComponent = ({
23096
23094
  ))
23097
23095
  }
23098
23096
  ) }) }),
23099
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
23100
- /* @__PURE__ */ jsxs("div", { className: "text-2xl sm:text-3xl lg:text-4xl font-bold text-gray-800", children: [
23101
- percentage,
23102
- "%"
23103
- ] }),
23104
- /* @__PURE__ */ jsxs("div", { className: "text-xs sm:text-sm lg:text-base text-gray-500 mt-1 sm:mt-2", children: [
23105
- currentOutput,
23106
- " / ",
23107
- Math.round(targetOutput)
23108
- ] })
23097
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", style: { width: "100%", padding: "0 10%" }, children: [
23098
+ /* @__PURE__ */ jsxs(
23099
+ "div",
23100
+ {
23101
+ className: "font-bold text-gray-800",
23102
+ style: { fontSize: "clamp(1.25rem, 8cqw, 2.5rem)" },
23103
+ children: [
23104
+ percentage,
23105
+ "%"
23106
+ ]
23107
+ }
23108
+ ),
23109
+ /* @__PURE__ */ jsxs(
23110
+ "div",
23111
+ {
23112
+ className: "text-gray-500",
23113
+ style: {
23114
+ fontSize: "clamp(0.7rem, 3.5cqw, 1rem)",
23115
+ marginTop: "clamp(0.125rem, 1cqw, 0.5rem)"
23116
+ },
23117
+ children: [
23118
+ currentOutput,
23119
+ " / ",
23120
+ Math.round(targetOutput)
23121
+ ]
23122
+ }
23123
+ )
23109
23124
  ] }) })
23110
23125
  ] }) });
23111
23126
  };
@@ -23123,7 +23138,7 @@ var LargeOutputProgressChart = ({
23123
23138
  ];
23124
23139
  const COLORS = ["#00AB45", "#f3f4f6"];
23125
23140
  const percentage = (currentOutput / targetOutput * 100).toFixed(1);
23126
- return /* @__PURE__ */ jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full", style: { minHeight: "100px", minWidth: "100px" }, children: [
23141
+ return /* @__PURE__ */ jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full", style: { minHeight: "100px", minWidth: "100px", containerType: "inline-size" }, children: [
23127
23142
  /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsx(PieChart, { children: /* @__PURE__ */ jsx(
23128
23143
  Pie,
23129
23144
  {
@@ -23147,16 +23162,40 @@ var LargeOutputProgressChart = ({
23147
23162
  ))
23148
23163
  }
23149
23164
  ) }) }),
23150
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
23151
- /* @__PURE__ */ jsx("div", { className: "text-4xl sm:text-5xl font-bold text-gray-900", children: currentOutput }),
23152
- /* @__PURE__ */ jsxs("div", { className: "text-sm text-gray-500", children: [
23153
- "of ",
23154
- targetOutput
23155
- ] }),
23156
- /* @__PURE__ */ jsxs("div", { className: "text-base font-medium text-gray-600 mt-1", children: [
23157
- percentage,
23158
- "%"
23159
- ] })
23165
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", style: { width: "100%", padding: "0 10%" }, children: [
23166
+ /* @__PURE__ */ jsx(
23167
+ "div",
23168
+ {
23169
+ className: "font-bold text-gray-900",
23170
+ style: { fontSize: "clamp(1.5rem, 10cqw, 3rem)" },
23171
+ children: currentOutput
23172
+ }
23173
+ ),
23174
+ /* @__PURE__ */ jsxs(
23175
+ "div",
23176
+ {
23177
+ className: "text-gray-500",
23178
+ style: { fontSize: "clamp(0.75rem, 3.5cqw, 1rem)" },
23179
+ children: [
23180
+ "of ",
23181
+ targetOutput
23182
+ ]
23183
+ }
23184
+ ),
23185
+ /* @__PURE__ */ jsxs(
23186
+ "div",
23187
+ {
23188
+ className: "font-medium text-gray-600",
23189
+ style: {
23190
+ fontSize: "clamp(0.875rem, 4.5cqw, 1.25rem)",
23191
+ marginTop: "clamp(0.125rem, 1cqw, 0.5rem)"
23192
+ },
23193
+ children: [
23194
+ percentage,
23195
+ "%"
23196
+ ]
23197
+ }
23198
+ )
23160
23199
  ] }) })
23161
23200
  ] }) });
23162
23201
  };
@@ -25250,7 +25289,7 @@ var GaugeChart = ({
25250
25289
  };
25251
25290
  const gaugeColor = getColor();
25252
25291
  const targetAngle = target !== void 0 ? 180 - (target - min) / (max - min) * 180 : null;
25253
- return /* @__PURE__ */ jsx("div", { className: `relative w-full h-full flex flex-col items-center justify-center ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-[280px] aspect-square", style: { minHeight: "100px", minWidth: "100px" }, children: [
25292
+ return /* @__PURE__ */ jsx("div", { className: `relative w-full h-full flex flex-col items-center justify-center ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "relative w-full max-w-[280px] aspect-square", style: { minHeight: "100px", minWidth: "100px", containerType: "inline-size" }, children: [
25254
25293
  /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsx(PieChart, { children: /* @__PURE__ */ jsxs(
25255
25294
  Pie,
25256
25295
  {
@@ -25283,17 +25322,44 @@ var GaugeChart = ({
25283
25322
  children: /* @__PURE__ */ jsx("div", { className: "absolute -top-1 -left-1.5 w-3 h-3 bg-gray-800 rounded-full" })
25284
25323
  }
25285
25324
  ),
25286
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
25287
- /* @__PURE__ */ jsxs("div", { className: "text-3xl font-bold text-gray-800", children: [
25288
- value.toFixed(unit === "%" ? 1 : 0),
25289
- unit
25290
- ] }),
25291
- /* @__PURE__ */ jsx("div", { className: "text-sm text-gray-600 mt-1 font-medium", children: label }),
25292
- target !== void 0 && /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500 mt-1", children: [
25293
- "Target: ",
25294
- target,
25295
- unit
25296
- ] })
25325
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", style: { width: "100%", padding: "0 10%" }, children: [
25326
+ /* @__PURE__ */ jsxs(
25327
+ "div",
25328
+ {
25329
+ className: "font-bold text-gray-800",
25330
+ style: { fontSize: "clamp(1.25rem, 8cqw, 2rem)" },
25331
+ children: [
25332
+ value.toFixed(unit === "%" ? 1 : 0),
25333
+ unit
25334
+ ]
25335
+ }
25336
+ ),
25337
+ /* @__PURE__ */ jsx(
25338
+ "div",
25339
+ {
25340
+ className: "text-gray-600 font-medium",
25341
+ style: {
25342
+ fontSize: "clamp(0.75rem, 3.5cqw, 1rem)",
25343
+ marginTop: "clamp(0.125rem, 1cqw, 0.25rem)"
25344
+ },
25345
+ children: label
25346
+ }
25347
+ ),
25348
+ target !== void 0 && /* @__PURE__ */ jsxs(
25349
+ "div",
25350
+ {
25351
+ className: "text-gray-500",
25352
+ style: {
25353
+ fontSize: "clamp(0.7rem, 3cqw, 0.875rem)",
25354
+ marginTop: "clamp(0.125rem, 1cqw, 0.25rem)"
25355
+ },
25356
+ children: [
25357
+ "Target: ",
25358
+ target,
25359
+ unit
25360
+ ]
25361
+ }
25362
+ )
25297
25363
  ] }) }),
25298
25364
  /* @__PURE__ */ jsxs("div", { className: "absolute bottom-[15%] left-[15%] text-xs text-gray-500", children: [
25299
25365
  min,
@@ -26024,12 +26090,6 @@ var AxelNotificationPopup = ({
26024
26090
  };
26025
26091
 
26026
26092
  // src/views/components/workspace/BottlenecksContent.utils.ts
26027
- var formatTime2 = (seconds) => {
26028
- if (isNaN(seconds)) return "0:00";
26029
- const minutes = Math.floor(seconds / 60);
26030
- const remainingSeconds = Math.floor(seconds % 60);
26031
- return `${minutes}:${remainingSeconds < 10 ? "0" : ""}${remainingSeconds}`;
26032
- };
26033
26093
  var getSeverityColor = (severity) => {
26034
26094
  switch (severity) {
26035
26095
  case "low":
@@ -26042,957 +26102,575 @@ var getSeverityColor = (severity) => {
26042
26102
  return "bg-gray-500";
26043
26103
  }
26044
26104
  };
26045
- function Skeleton({ className, ...props }) {
26046
- return /* @__PURE__ */ jsx("div", { className: cn("animate-pulse rounded-md bg-muted", className), ...props });
26047
- }
26048
- var Select = SelectPrimitive.Root;
26049
- var SelectGroup = SelectPrimitive.Group;
26050
- var SelectValue = SelectPrimitive.Value;
26051
- var SelectTrigger = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
26052
- SelectPrimitive.Trigger,
26053
- {
26054
- ref,
26055
- className: cn(
26056
- "flex h-11 sm:h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 touch-manipulation",
26057
- className
26058
- ),
26059
- ...props,
26060
- children: [
26061
- children,
26062
- /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
26063
- ]
26064
- }
26065
- ));
26066
- SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
26067
- var SelectScrollUpButton = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
26068
- SelectPrimitive.ScrollUpButton,
26069
- {
26070
- ref,
26071
- className: cn("flex cursor-default items-center justify-center py-1", className),
26072
- ...props,
26073
- children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
26074
- }
26075
- ));
26076
- SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
26077
- var SelectScrollDownButton = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
26078
- SelectPrimitive.ScrollDownButton,
26079
- {
26080
- ref,
26081
- className: cn("flex cursor-default items-center justify-center py-1", className),
26082
- ...props,
26083
- children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
26084
- }
26085
- ));
26086
- SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
26087
- var SelectContent = React23.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
26088
- SelectPrimitive.Content,
26089
- {
26090
- ref,
26091
- className: cn(
26092
- "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
26093
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
26094
- className
26095
- ),
26096
- position,
26097
- ...props,
26098
- children: [
26099
- /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
26100
- /* @__PURE__ */ jsx(
26101
- SelectPrimitive.Viewport,
26102
- {
26103
- className: cn(
26104
- "p-1",
26105
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
26106
- ),
26107
- children
26108
- }
26109
- ),
26110
- /* @__PURE__ */ jsx(SelectScrollDownButton, {})
26111
- ]
26112
- }
26113
- ) }));
26114
- SelectContent.displayName = SelectPrimitive.Content.displayName;
26115
- var SelectLabel = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
26116
- SelectPrimitive.Label,
26117
- {
26118
- ref,
26119
- className: cn("px-2 py-1.5 text-sm font-semibold", className),
26120
- ...props
26121
- }
26122
- ));
26123
- SelectLabel.displayName = SelectPrimitive.Label.displayName;
26124
- var SelectItem = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
26125
- SelectPrimitive.Item,
26126
- {
26127
- ref,
26128
- className: cn(
26129
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
26130
- className
26131
- ),
26132
- ...props,
26133
- children: [
26134
- /* @__PURE__ */ jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
26135
- /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
26136
- ]
26137
- }
26138
- ));
26139
- SelectItem.displayName = SelectPrimitive.Item.displayName;
26140
- var SelectSeparator = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
26141
- SelectPrimitive.Separator,
26142
- {
26143
- ref,
26144
- className: cn("-mx-1 my-1 h-px bg-muted", className),
26145
- ...props
26146
- }
26147
- ));
26148
- SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
26149
- var LoadingOverlay = ({
26150
- isVisible,
26151
- message = "Loading...",
26152
- className
26105
+ var PlayPauseIndicator = ({
26106
+ show,
26107
+ isPlaying,
26108
+ duration = 600
26153
26109
  }) => {
26110
+ const [isVisible, setIsVisible] = useState(false);
26111
+ const [isFading, setIsFading] = useState(false);
26112
+ useEffect(() => {
26113
+ if (show) {
26114
+ setIsVisible(true);
26115
+ setIsFading(false);
26116
+ const fadeTimer = setTimeout(() => {
26117
+ setIsFading(true);
26118
+ }, 100);
26119
+ const hideTimer = setTimeout(() => {
26120
+ setIsVisible(false);
26121
+ setIsFading(false);
26122
+ }, duration);
26123
+ return () => {
26124
+ clearTimeout(fadeTimer);
26125
+ clearTimeout(hideTimer);
26126
+ };
26127
+ }
26128
+ }, [show, duration]);
26154
26129
  if (!isVisible) return null;
26155
26130
  return /* @__PURE__ */ jsx(
26156
- motion.div,
26131
+ "div",
26157
26132
  {
26158
- initial: { opacity: 0 },
26159
- animate: { opacity: 1 },
26160
- exit: { opacity: 0 },
26161
- transition: { duration: 0.2 },
26162
- className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
26163
- "aria-modal": "true",
26164
- role: "dialog",
26165
- children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message }) })
26133
+ className: "absolute inset-0 flex items-center justify-center pointer-events-none z-10",
26134
+ style: {
26135
+ opacity: isFading ? 0 : 1,
26136
+ transition: `opacity ${duration - 100}ms ease-out`
26137
+ },
26138
+ children: /* @__PURE__ */ jsx("div", { className: "bg-black/70 rounded-full p-6", children: isPlaying ? (
26139
+ // Play icon (triangle)
26140
+ /* @__PURE__ */ jsx(
26141
+ "svg",
26142
+ {
26143
+ xmlns: "http://www.w3.org/2000/svg",
26144
+ viewBox: "0 0 24 24",
26145
+ fill: "white",
26146
+ className: "w-16 h-16",
26147
+ children: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7z" })
26148
+ }
26149
+ )
26150
+ ) : (
26151
+ // Pause icon (two bars)
26152
+ /* @__PURE__ */ jsx(
26153
+ "svg",
26154
+ {
26155
+ xmlns: "http://www.w3.org/2000/svg",
26156
+ viewBox: "0 0 24 24",
26157
+ fill: "white",
26158
+ className: "w-16 h-16",
26159
+ children: /* @__PURE__ */ jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" })
26160
+ }
26161
+ )
26162
+ ) })
26166
26163
  }
26167
26164
  );
26168
26165
  };
26169
- var LoadingOverlay_default = LoadingOverlay;
26170
- var TimeDisplay = ({ className, variant = "default" }) => {
26171
- const { dateTimeConfig } = useDashboardConfig();
26172
- const [time2, setTime] = useState("");
26173
- const dbTimezone = useAppTimezone();
26174
- const timezoneToDisplay = dbTimezone || dateTimeConfig?.defaultTimezone || "UTC";
26175
- const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
26176
- const timeSuffix = "";
26177
- useEffect(() => {
26178
- const updateTime = () => {
26179
- const now2 = /* @__PURE__ */ new Date();
26180
- const effectiveFormatOptions = {
26181
- hour: "2-digit",
26182
- minute: "2-digit",
26183
- second: "2-digit",
26184
- hour12: true,
26185
- timeZone: timezoneToDisplay,
26186
- ...dateTimeConfig?.timeFormatOptions || {}
26187
- // Allow override from config
26188
- };
26189
- try {
26190
- setTime(new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2));
26191
- } catch (e) {
26192
- console.error("Error formatting time:", e);
26193
- setTime("Error");
26194
- }
26195
- };
26196
- updateTime();
26197
- const interval = setInterval(updateTime, 1e3);
26198
- return () => clearInterval(interval);
26199
- }, [timezoneToDisplay, dateTimeConfig?.timeFormatOptions, localeToUse]);
26200
- if (!time2) return null;
26201
- if (variant === "minimal") {
26202
- return /* @__PURE__ */ jsxs("span", { className: className || "", children: [
26203
- time2,
26204
- " ",
26205
- timeSuffix
26206
- ] });
26166
+ var ERROR_MAPPING = {
26167
+ "networkError": {
26168
+ code: 2,
26169
+ type: "recoverable",
26170
+ message: "Network error - please check your internet connection",
26171
+ canRetry: true
26172
+ },
26173
+ "mediaError": {
26174
+ code: 3,
26175
+ type: "non_recoverable",
26176
+ message: "Stream corrupted due to internet connection",
26177
+ canRetry: false
26178
+ },
26179
+ "muxError": {
26180
+ code: 3,
26181
+ type: "non_recoverable",
26182
+ message: "Error processing media stream",
26183
+ canRetry: false
26184
+ },
26185
+ "otherError": {
26186
+ code: 4,
26187
+ type: "non_recoverable",
26188
+ message: "Video format not supported by your browser. Please use Google Chrome.",
26189
+ canRetry: false
26207
26190
  }
26208
- return /* @__PURE__ */ jsxs(
26209
- motion.div,
26210
- {
26211
- initial: { opacity: 0, y: -5 },
26212
- animate: { opacity: 1, y: 0 },
26213
- transition: { duration: 0.3 },
26214
- className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
26215
- children: [
26216
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-[var(--primary-DEFAULT)]", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z", clipRule: "evenodd" }) }),
26217
- /* @__PURE__ */ jsxs("span", { className: "text-xs sm:text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
26218
- time2,
26219
- " ",
26220
- timeSuffix
26221
- ] })
26222
- ]
26223
- }
26224
- );
26225
26191
  };
26226
- var TimeDisplay_default = TimeDisplay;
26227
- var DateDisplay = ({ className, variant = "default" }) => {
26228
- const { dateTimeConfig } = useDashboardConfig();
26229
- const [date, setDate] = useState("");
26230
- const timezoneToDisplay = dateTimeConfig?.defaultTimezone || "UTC";
26231
- const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
26192
+ var hlsVideoPlayerStyles = `
26193
+ .hls-video-player-container {
26194
+ width: 100%;
26195
+ height: 100%;
26196
+ background-color: #000;
26197
+ display: flex;
26198
+ align-items: center;
26199
+ justify-content: center;
26200
+ }
26201
+
26202
+ /* Center the video and maintain aspect ratio */
26203
+ .hls-video-element {
26204
+ width: 100%;
26205
+ height: 100%;
26206
+ max-width: 100%;
26207
+ max-height: 100%;
26208
+ object-fit: contain;
26209
+ }
26210
+
26211
+ /* Custom loading indicator styles */
26212
+ .hls-video-player-loading {
26213
+ position: absolute;
26214
+ top: 50%;
26215
+ left: 50%;
26216
+ transform: translate(-50%, -50%);
26217
+ z-index: 10;
26218
+ }
26219
+ `;
26220
+ if (typeof document !== "undefined") {
26221
+ const styleId = "hls-video-player-custom-styles";
26222
+ if (!document.getElementById(styleId)) {
26223
+ const style = document.createElement("style");
26224
+ style.id = styleId;
26225
+ style.textContent = hlsVideoPlayerStyles;
26226
+ document.head.appendChild(style);
26227
+ }
26228
+ }
26229
+ var BASE_HLS_CONFIG = {
26230
+ maxBufferLength: 3,
26231
+ maxMaxBufferLength: 8,
26232
+ maxBufferSize: 50 * 1e3 * 1e3,
26233
+ maxBufferHole: 0.25,
26234
+ manifestLoadingTimeOut: 15e3,
26235
+ manifestLoadingMaxRetry: 3,
26236
+ manifestLoadingRetryDelay: 500,
26237
+ levelLoadingTimeOut: 6e4,
26238
+ levelLoadingMaxRetry: 5,
26239
+ levelLoadingRetryDelay: 500,
26240
+ fragLoadingTimeOut: 6e4,
26241
+ fragLoadingMaxRetry: 5,
26242
+ fragLoadingRetryDelay: 500,
26243
+ startPosition: -1,
26244
+ debug: false,
26245
+ enableWorker: true,
26246
+ lowLatencyMode: false,
26247
+ progressive: true,
26248
+ abrEwmaSlowLive: 9,
26249
+ abrEwmaFastLive: 3,
26250
+ abrBandWidthFactor: 0.95,
26251
+ abrBandWidthUpFactor: 0.7,
26252
+ abrMaxWithRealBitrate: false,
26253
+ abrEwmaDefaultEstimate: 5e7
26254
+ };
26255
+ var HlsVideoPlayer = forwardRef(({
26256
+ src,
26257
+ poster,
26258
+ autoplay = false,
26259
+ controls = true,
26260
+ loop = false,
26261
+ muted = false,
26262
+ playsInline = true,
26263
+ className = "",
26264
+ hlsConfig = {},
26265
+ options = {},
26266
+ // Backward compatibility with Video.js
26267
+ externalLoadingControl = false,
26268
+ onLoadingChange,
26269
+ onReady,
26270
+ onPlay,
26271
+ onPause,
26272
+ onPlaying,
26273
+ onTimeUpdate,
26274
+ onDurationChange,
26275
+ onEnded,
26276
+ onError,
26277
+ onLoadStart,
26278
+ onLoadedMetadata,
26279
+ onLoadedData,
26280
+ onSeeking,
26281
+ onSeeked,
26282
+ onClick
26283
+ }, ref) => {
26284
+ const videoContainerRef = useRef(null);
26285
+ const videoRef = useRef(null);
26286
+ const hlsRef = useRef(null);
26287
+ const blobUrlRef = useRef(null);
26288
+ const [isReady, setIsReady] = useState(false);
26289
+ const [isLoading, setIsLoading] = useState(true);
26290
+ const [showIndicator, setShowIndicator] = useState(false);
26291
+ const [indicatorIsPlaying, setIndicatorIsPlaying] = useState(false);
26292
+ const indicatorKeyRef = useRef(0);
26293
+ const eventCallbacksRef = useRef({
26294
+ onReady,
26295
+ onPlay,
26296
+ onPause,
26297
+ onPlaying,
26298
+ onTimeUpdate,
26299
+ onDurationChange,
26300
+ onEnded,
26301
+ onError,
26302
+ onLoadStart,
26303
+ onLoadedMetadata,
26304
+ onLoadedData,
26305
+ onSeeking,
26306
+ onSeeked,
26307
+ onLoadingChange
26308
+ });
26232
26309
  useEffect(() => {
26233
- const getCurrentFormattedDate = () => {
26234
- const now2 = /* @__PURE__ */ new Date();
26235
- const effectiveFormatOptions = {
26236
- weekday: "short",
26237
- day: "numeric",
26238
- month: "short",
26239
- timeZone: timezoneToDisplay,
26240
- ...dateTimeConfig?.dateFormatOptions || {}
26241
- // Allow override from config
26242
- };
26243
- try {
26244
- return new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2);
26245
- } catch (e) {
26246
- console.error("Error formatting date:", e);
26247
- return "Error";
26248
- }
26310
+ eventCallbacksRef.current = {
26311
+ onReady,
26312
+ onPlay,
26313
+ onPause,
26314
+ onPlaying,
26315
+ onTimeUpdate,
26316
+ onDurationChange,
26317
+ onEnded,
26318
+ onError,
26319
+ onLoadStart,
26320
+ onLoadedMetadata,
26321
+ onLoadedData,
26322
+ onSeeking,
26323
+ onSeeked,
26324
+ onLoadingChange
26249
26325
  };
26250
- const updateDate = () => {
26251
- setDate(getCurrentFormattedDate());
26326
+ }, [
26327
+ onReady,
26328
+ onPlay,
26329
+ onPause,
26330
+ onPlaying,
26331
+ onTimeUpdate,
26332
+ onDurationChange,
26333
+ onEnded,
26334
+ onError,
26335
+ onLoadStart,
26336
+ onLoadedMetadata,
26337
+ onLoadedData,
26338
+ onSeeking,
26339
+ onSeeked,
26340
+ onLoadingChange
26341
+ ]);
26342
+ const stableHlsConfigRef = useRef(hlsConfig);
26343
+ const stableOptionsRef = useRef(options);
26344
+ const configSignatureRef = useRef("");
26345
+ const [configVersion, setConfigVersion] = useState(0);
26346
+ useEffect(() => {
26347
+ const serialized = JSON.stringify({
26348
+ hlsConfig: hlsConfig || null,
26349
+ options: options || null
26350
+ });
26351
+ if (!configSignatureRef.current) {
26352
+ configSignatureRef.current = serialized;
26353
+ stableHlsConfigRef.current = hlsConfig;
26354
+ stableOptionsRef.current = options;
26355
+ return;
26356
+ }
26357
+ if (configSignatureRef.current !== serialized) {
26358
+ configSignatureRef.current = serialized;
26359
+ stableHlsConfigRef.current = hlsConfig;
26360
+ stableOptionsRef.current = options;
26361
+ setConfigVersion((prev) => prev + 1);
26362
+ }
26363
+ }, [hlsConfig, options]);
26364
+ const cleanupBlobUrl = useCallback(() => {
26365
+ if (blobUrlRef.current) {
26366
+ URL.revokeObjectURL(blobUrlRef.current);
26367
+ blobUrlRef.current = null;
26368
+ }
26369
+ }, []);
26370
+ const dispose = useCallback(() => {
26371
+ if (hlsRef.current) {
26372
+ hlsRef.current.destroy();
26373
+ hlsRef.current = null;
26374
+ }
26375
+ cleanupBlobUrl();
26376
+ setIsReady(false);
26377
+ }, [cleanupBlobUrl]);
26378
+ const playerLikeObject = useCallback(() => {
26379
+ return {
26380
+ el: () => videoRef.current,
26381
+ currentTime: () => videoRef.current?.currentTime || 0,
26382
+ duration: () => videoRef.current?.duration || 0,
26383
+ paused: () => videoRef.current?.paused ?? true,
26384
+ play: () => videoRef.current?.play(),
26385
+ pause: () => videoRef.current?.pause(),
26386
+ muted: (val) => {
26387
+ if (videoRef.current) {
26388
+ if (val !== void 0) videoRef.current.muted = val;
26389
+ return videoRef.current.muted;
26390
+ }
26391
+ return false;
26392
+ },
26393
+ volume: (val) => {
26394
+ if (videoRef.current) {
26395
+ if (val !== void 0) videoRef.current.volume = val;
26396
+ return videoRef.current.volume;
26397
+ }
26398
+ return 1;
26399
+ },
26400
+ error: () => null,
26401
+ dispose: () => dispose()
26252
26402
  };
26253
- updateDate();
26254
- const interval = setInterval(() => {
26255
- const currentDateStr = getCurrentFormattedDate();
26256
- if (currentDateStr !== date) {
26257
- updateDate();
26403
+ }, [dispose]);
26404
+ const initializePlayer = useCallback(() => {
26405
+ if (!videoRef.current || !src) return;
26406
+ const video = videoRef.current;
26407
+ const player = playerLikeObject();
26408
+ const mergedHlsConfig = {
26409
+ ...BASE_HLS_CONFIG,
26410
+ ...stableHlsConfigRef.current || {},
26411
+ ...stableOptionsRef.current || {}
26412
+ };
26413
+ cleanupBlobUrl();
26414
+ const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
26415
+ if (isHLS) {
26416
+ if (src.startsWith("#EXTM3U")) {
26417
+ const safariMode = isSafari();
26418
+ const browserName = getBrowserName();
26419
+ if (safariMode) {
26420
+ const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
26421
+ if (clipIdMatch) {
26422
+ const proxyUrl = `/api/clips/playlist/${clipIdMatch[1]}`;
26423
+ console.log(`[HlsVideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, proxyUrl);
26424
+ video.src = proxyUrl;
26425
+ } else {
26426
+ console.warn("[HlsVideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
26427
+ const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26428
+ const blobUrl = URL.createObjectURL(blob);
26429
+ blobUrlRef.current = blobUrl;
26430
+ video.src = blobUrl;
26431
+ }
26432
+ } else if (Hls3.isSupported()) {
26433
+ const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26434
+ const blobUrl = URL.createObjectURL(blob);
26435
+ blobUrlRef.current = blobUrl;
26436
+ console.log(`[HlsVideoPlayer] Non-Safari browser (${browserName}) - using HLS.js with blob URL`);
26437
+ const hls = new Hls3(mergedHlsConfig);
26438
+ hlsRef.current = hls;
26439
+ hls.on(Events.MANIFEST_PARSED, () => {
26440
+ console.log("[HlsVideoPlayer] Manifest parsed, ready to play");
26441
+ setIsReady(true);
26442
+ eventCallbacksRef.current.onReady?.(player);
26443
+ });
26444
+ hls.on(Events.ERROR, (event, data) => {
26445
+ console.error("[HlsVideoPlayer] HLS.js error:", data);
26446
+ if (data.fatal) {
26447
+ let errorInfo;
26448
+ switch (data.type) {
26449
+ case ErrorTypes.NETWORK_ERROR:
26450
+ errorInfo = ERROR_MAPPING.networkError;
26451
+ console.log("[HlsVideoPlayer] Attempting to recover from network error");
26452
+ hls.startLoad();
26453
+ break;
26454
+ case ErrorTypes.MEDIA_ERROR:
26455
+ errorInfo = ERROR_MAPPING.mediaError;
26456
+ console.log("[HlsVideoPlayer] Attempting to recover from media error");
26457
+ hls.recoverMediaError();
26458
+ break;
26459
+ case ErrorTypes.MUX_ERROR:
26460
+ errorInfo = ERROR_MAPPING.muxError;
26461
+ break;
26462
+ default:
26463
+ errorInfo = ERROR_MAPPING.otherError;
26464
+ break;
26465
+ }
26466
+ errorInfo.details = data.details;
26467
+ eventCallbacksRef.current.onError?.(player, errorInfo);
26468
+ }
26469
+ });
26470
+ hls.on(Events.FRAG_LOADING, () => {
26471
+ setIsLoading(true);
26472
+ eventCallbacksRef.current.onLoadingChange?.(true);
26473
+ });
26474
+ hls.on(Events.FRAG_LOADED, () => {
26475
+ setIsLoading(false);
26476
+ eventCallbacksRef.current.onLoadingChange?.(false);
26477
+ });
26478
+ hls.loadSource(blobUrl);
26479
+ hls.attachMedia(video);
26480
+ } else {
26481
+ console.error("[HlsVideoPlayer] HLS.js not supported and not Safari - cannot play HLS content");
26482
+ const errorInfo = ERROR_MAPPING.otherError;
26483
+ onError?.(player, errorInfo);
26484
+ }
26485
+ } else {
26486
+ if (Hls3.isSupported() && !isSafari()) {
26487
+ const hls = new Hls3(mergedHlsConfig);
26488
+ hlsRef.current = hls;
26489
+ hls.on(Events.MANIFEST_PARSED, () => {
26490
+ setIsReady(true);
26491
+ eventCallbacksRef.current.onReady?.(player);
26492
+ });
26493
+ hls.on(Events.ERROR, (event, data) => {
26494
+ console.error("[HlsVideoPlayer] HLS.js error:", data);
26495
+ if (data.fatal) {
26496
+ let errorInfo;
26497
+ switch (data.type) {
26498
+ case ErrorTypes.NETWORK_ERROR:
26499
+ errorInfo = ERROR_MAPPING.networkError;
26500
+ hls.startLoad();
26501
+ break;
26502
+ case ErrorTypes.MEDIA_ERROR:
26503
+ errorInfo = ERROR_MAPPING.mediaError;
26504
+ hls.recoverMediaError();
26505
+ break;
26506
+ default:
26507
+ errorInfo = ERROR_MAPPING.otherError;
26508
+ break;
26509
+ }
26510
+ errorInfo.details = data.details;
26511
+ eventCallbacksRef.current.onError?.(player, errorInfo);
26512
+ }
26513
+ });
26514
+ hls.loadSource(src);
26515
+ hls.attachMedia(video);
26516
+ } else {
26517
+ video.src = src;
26518
+ }
26258
26519
  }
26259
- }, 60 * 1e3);
26260
- return () => clearInterval(interval);
26261
- }, [date, timezoneToDisplay, dateTimeConfig?.dateFormatOptions, localeToUse]);
26262
- if (!date) return null;
26263
- if (variant === "minimal") {
26264
- return /* @__PURE__ */ jsx("span", { className: className || "", children: date });
26265
- }
26266
- return /* @__PURE__ */ jsxs(
26267
- motion.div,
26268
- {
26269
- initial: { opacity: 0, y: -5 },
26270
- animate: { opacity: 1, y: 0 },
26271
- transition: { duration: 0.3 },
26272
- className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
26273
- children: [
26274
- /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-blue-600", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z", clipRule: "evenodd" }) }),
26275
- /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-gray-800 tracking-tight", children: date })
26276
- ]
26520
+ } else {
26521
+ video.src = src;
26277
26522
  }
26278
- );
26279
- };
26280
- var DateDisplay_default = DateDisplay;
26281
- var Card3 = Card2;
26282
- var CardHeader3 = CardHeader2;
26283
- var CardTitle3 = CardTitle2;
26284
- var CardContent3 = CardContent2;
26285
- var MetricCard2 = ({ title, value, unit = "", trend = null }) => {
26286
- const getTrendColor = (trendValue) => {
26287
- if (trendValue === null || trendValue === void 0) return "";
26288
- return trendValue > 0 ? "text-green-500" : trendValue < 0 ? "text-red-500" : "text-gray-500";
26289
- };
26290
- const getTrendSymbol = (trendValue) => {
26291
- if (trendValue === null || trendValue === void 0) return "";
26292
- return trendValue > 0 ? "\u2191" : trendValue < 0 ? "\u2193" : "";
26293
- };
26294
- return /* @__PURE__ */ jsxs(Card3, { className: "bg-white", children: [
26295
- /* @__PURE__ */ jsx(CardHeader3, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle3, { className: "text-sm font-medium text-gray-500", children: title }) }),
26296
- /* @__PURE__ */ jsx(CardContent3, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
26297
- /* @__PURE__ */ jsxs("div", { className: "text-2xl font-semibold", children: [
26298
- value,
26299
- unit && /* @__PURE__ */ jsx("span", { className: "ml-1 text-sm text-gray-500", children: unit })
26300
- ] }),
26301
- trend !== null && trend !== void 0 && // Check trend for null/undefined before accessing
26302
- /* @__PURE__ */ jsx("div", { className: `flex items-center ${getTrendColor(trend)}`, children: /* @__PURE__ */ jsxs("span", { className: "text-sm", children: [
26303
- getTrendSymbol(trend),
26304
- " ",
26305
- Math.abs(trend),
26306
- "%"
26307
- ] }) })
26308
- ] }) })
26309
- ] });
26310
- };
26311
- var MetricCard_default = MetricCard2;
26312
- var TimePickerDropdown = ({
26313
- value,
26314
- onChange,
26315
- placeholder = "Select time",
26316
- className = "",
26317
- disabled = false
26318
- }) => {
26319
- const [isOpen, setIsOpen] = useState(false);
26320
- const [searchTerm, setSearchTerm] = useState("");
26321
- const dropdownRef = useRef(null);
26322
- const inputRef = useRef(null);
26323
- const generateTimeSlots = () => {
26324
- const slots = [];
26325
- for (let hour = 0; hour < 24; hour++) {
26326
- for (let minute = 0; minute < 60; minute += 15) {
26327
- const time24 = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
26328
- const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
26329
- const ampm = hour < 12 ? "AM" : "PM";
26330
- const time12 = `${hour12}:${minute.toString().padStart(2, "0")} ${ampm}`;
26331
- slots.push({ value: time24, label: time12 });
26332
- }
26333
- }
26334
- return slots;
26335
- };
26336
- const timeSlots = generateTimeSlots();
26337
- const filteredSlots = timeSlots.filter(
26338
- (slot) => slot.label.toLowerCase().includes(searchTerm.toLowerCase())
26339
- );
26340
- const getDisplayValue = (value2) => {
26341
- if (!value2) return "";
26342
- const normalizedValue = value2.substring(0, 5);
26343
- const slot = timeSlots.find((s) => s.value === normalizedValue);
26344
- return slot ? slot.label : value2;
26345
- };
26346
- useEffect(() => {
26347
- const handleClickOutside = (event) => {
26348
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
26349
- setIsOpen(false);
26350
- setSearchTerm("");
26523
+ const handleCanPlay = () => {
26524
+ if (!hlsRef.current) {
26525
+ setIsReady(true);
26526
+ onReady?.(player);
26351
26527
  }
26352
26528
  };
26353
- document.addEventListener("mousedown", handleClickOutside);
26354
- return () => document.removeEventListener("mousedown", handleClickOutside);
26355
- }, []);
26356
- const handleKeyDown = (e) => {
26357
- if (e.key === "Escape") {
26358
- setIsOpen(false);
26359
- setSearchTerm("");
26360
- } else if (e.key === "Enter") {
26361
- e.preventDefault();
26362
- if (filteredSlots.length > 0) {
26363
- onChange(filteredSlots[0].value);
26364
- setIsOpen(false);
26365
- setSearchTerm("");
26366
- }
26367
- }
26368
- };
26369
- const handleSelect = (timeValue) => {
26370
- onChange(timeValue);
26371
- setIsOpen(false);
26372
- setSearchTerm("");
26373
- };
26374
- const handleToggle = () => {
26375
- if (disabled) return;
26376
- setIsOpen(!isOpen);
26377
- if (!isOpen) {
26378
- setTimeout(() => inputRef.current?.focus(), 100);
26379
- }
26380
- };
26381
- return /* @__PURE__ */ jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [
26382
- /* @__PURE__ */ jsx(
26383
- "button",
26384
- {
26385
- type: "button",
26386
- onClick: handleToggle,
26387
- disabled,
26388
- className: `
26389
- w-full px-3 py-2 text-left bg-white border border-gray-300 rounded-md shadow-sm
26390
- focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
26391
- hover:border-gray-400 transition-colors duration-200
26392
- ${disabled ? "bg-gray-50 cursor-not-allowed" : "cursor-pointer"}
26393
- ${isOpen ? "ring-2 ring-blue-500 border-blue-500" : ""}
26394
- `,
26395
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
26396
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
26397
- /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-gray-400" }),
26398
- /* @__PURE__ */ jsx("span", { className: `text-sm ${value ? "text-gray-900" : "text-gray-500"}`, children: value ? getDisplayValue(value) : placeholder })
26399
- ] }),
26400
- /* @__PURE__ */ jsx(
26401
- ChevronDown,
26402
- {
26403
- className: `h-4 w-4 text-gray-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
26404
- }
26405
- )
26406
- ] })
26407
- }
26408
- ),
26409
- isOpen && /* @__PURE__ */ jsxs("div", { className: "absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-hidden", children: [
26410
- /* @__PURE__ */ jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsx(
26411
- "input",
26412
- {
26413
- ref: inputRef,
26414
- type: "text",
26415
- placeholder: "Search time...",
26416
- value: searchTerm,
26417
- onChange: (e) => setSearchTerm(e.target.value),
26418
- onKeyDown: handleKeyDown,
26419
- className: "w-full px-3 py-2 text-sm border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
26420
- }
26421
- ) }),
26422
- /* @__PURE__ */ jsx("div", { className: "overflow-y-auto max-h-48", children: filteredSlots.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: "No times found" }) : filteredSlots.map((slot) => /* @__PURE__ */ jsx(
26423
- "button",
26424
- {
26425
- type: "button",
26426
- onClick: () => handleSelect(slot.value),
26427
- className: `
26428
- w-full px-3 py-2 text-left text-sm hover:bg-blue-50 hover:text-blue-600
26429
- transition-colors duration-150 border-b border-gray-100 last:border-b-0
26430
- ${slot.value === value ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-700"}
26431
- `,
26432
- children: slot.label
26433
- },
26434
- slot.value
26435
- )) })
26436
- ] })
26437
- ] });
26438
- };
26439
- var SilentErrorBoundary = class extends React23__default.Component {
26440
- constructor(props) {
26441
- super(props);
26442
- this.handleClearAndReload = () => {
26443
- console.log("[ErrorBoundary] User initiated reset");
26444
- if (typeof window !== "undefined") {
26445
- try {
26446
- localStorage.clear();
26447
- sessionStorage.clear();
26448
- console.log("[ErrorBoundary] Cleared all storage");
26449
- } catch (error) {
26450
- console.error("[ErrorBoundary] Failed to clear storage:", error);
26451
- }
26452
- }
26453
- window.location.href = "/login";
26529
+ const handlePlay = () => eventCallbacksRef.current.onPlay?.(player);
26530
+ const handlePause = () => eventCallbacksRef.current.onPause?.(player);
26531
+ const handlePlaying = () => {
26532
+ setIsLoading(false);
26533
+ eventCallbacksRef.current.onLoadingChange?.(false);
26534
+ eventCallbacksRef.current.onPlaying?.(player);
26454
26535
  };
26455
- this.state = {
26456
- hasError: false,
26457
- errorCount: 0,
26458
- lastError: null,
26459
- errorInfo: null
26536
+ const handleTimeUpdate = () => {
26537
+ const currentTime2 = video.currentTime || 0;
26538
+ eventCallbacksRef.current.onTimeUpdate?.(player, currentTime2);
26460
26539
  };
26461
- }
26462
- static getDerivedStateFromError(error) {
26463
- return { hasError: true };
26464
- }
26465
- componentDidCatch(error, errorInfo) {
26466
- console.error("[ErrorBoundary] Caught render error:", {
26467
- error: error.message,
26468
- stack: error.stack,
26469
- componentStack: errorInfo.componentStack,
26470
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
26471
- });
26472
- this.setState((prev) => ({
26473
- errorCount: prev.errorCount + 1,
26474
- lastError: error,
26475
- errorInfo
26476
- }));
26477
- try {
26478
- if (typeof window !== "undefined" && window.mixpanel) {
26479
- window.mixpanel.track("React Render Error", {
26480
- error: error.message,
26481
- component: errorInfo.componentStack?.split("\n")[1] || "unknown",
26482
- errorCount: this.state.errorCount + 1
26483
- });
26484
- }
26485
- } catch (analyticsError) {
26486
- console.warn("[ErrorBoundary] Analytics tracking failed:", analyticsError);
26487
- }
26488
- }
26489
- render() {
26490
- if (!this.state.hasError) {
26491
- return this.props.children;
26492
- }
26493
- return /* @__PURE__ */ jsx("div", { className: "flex h-screen w-screen items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center space-y-6 text-center", children: [
26494
- /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading Dashboard..." }),
26495
- /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Taking longer than usual..." }),
26496
- /* @__PURE__ */ jsx("div", { className: "pt-4", children: /* @__PURE__ */ jsx(
26497
- "a",
26498
- {
26499
- href: "#",
26500
- onClick: (e) => {
26501
- e.preventDefault();
26502
- this.handleClearAndReload();
26503
- },
26504
- className: "text-xs text-gray-400 hover:text-gray-600 underline transition-colors",
26505
- children: "Reset and try again"
26506
- }
26507
- ) }),
26508
- process.env.NODE_ENV === "development" && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 italic mt-4", children: "Check console for error details" })
26509
- ] }) });
26510
- }
26511
- };
26512
- var PlayPauseIndicator = ({
26513
- show,
26514
- isPlaying,
26515
- duration = 600
26516
- }) => {
26517
- const [isVisible, setIsVisible] = useState(false);
26518
- const [isFading, setIsFading] = useState(false);
26519
- useEffect(() => {
26520
- if (show) {
26521
- setIsVisible(true);
26522
- setIsFading(false);
26523
- const fadeTimer = setTimeout(() => {
26524
- setIsFading(true);
26525
- }, 100);
26526
- const hideTimer = setTimeout(() => {
26527
- setIsVisible(false);
26528
- setIsFading(false);
26529
- }, duration);
26530
- return () => {
26531
- clearTimeout(fadeTimer);
26532
- clearTimeout(hideTimer);
26533
- };
26534
- }
26535
- }, [show, duration]);
26536
- if (!isVisible) return null;
26537
- return /* @__PURE__ */ jsx(
26538
- "div",
26539
- {
26540
- className: "absolute inset-0 flex items-center justify-center pointer-events-none z-10",
26541
- style: {
26542
- opacity: isFading ? 0 : 1,
26543
- transition: `opacity ${duration - 100}ms ease-out`
26544
- },
26545
- children: /* @__PURE__ */ jsx("div", { className: "bg-black/70 rounded-full p-6", children: isPlaying ? (
26546
- // Play icon (triangle)
26547
- /* @__PURE__ */ jsx(
26548
- "svg",
26549
- {
26550
- xmlns: "http://www.w3.org/2000/svg",
26551
- viewBox: "0 0 24 24",
26552
- fill: "white",
26553
- className: "w-16 h-16",
26554
- children: /* @__PURE__ */ jsx("path", { d: "M8 5v14l11-7z" })
26555
- }
26556
- )
26557
- ) : (
26558
- // Pause icon (two bars)
26559
- /* @__PURE__ */ jsx(
26560
- "svg",
26561
- {
26562
- xmlns: "http://www.w3.org/2000/svg",
26563
- viewBox: "0 0 24 24",
26564
- fill: "white",
26565
- className: "w-16 h-16",
26566
- children: /* @__PURE__ */ jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" })
26567
- }
26568
- )
26569
- ) })
26570
- }
26571
- );
26572
- };
26573
- var ERROR_MAPPING = {
26574
- 1: {
26575
- // MEDIA_ERR_ABORTED
26576
- code: 1,
26577
- type: "recoverable" /* RECOVERABLE */,
26578
- message: "Video loading was interrupted",
26579
- canRetry: true
26580
- },
26581
- 2: {
26582
- // MEDIA_ERR_NETWORK
26583
- code: 2,
26584
- type: "recoverable" /* RECOVERABLE */,
26585
- message: "Network error - please check your internet connection",
26586
- canRetry: true
26587
- },
26588
- 3: {
26589
- // MEDIA_ERR_DECODE
26590
- code: 3,
26591
- type: "non_recoverable" /* NON_RECOVERABLE */,
26592
- message: "Stream corrupted due to internet connection",
26593
- canRetry: false
26594
- },
26595
- 4: {
26596
- // MEDIA_ERR_SRC_NOT_SUPPORTED
26597
- code: 4,
26598
- type: "non_recoverable" /* NON_RECOVERABLE */,
26599
- message: "Video format not supported by your browser. Please use Google Chrome.",
26600
- canRetry: false
26601
- }
26602
- };
26603
- var videoPlayerStyles = `
26604
- .video-player-container {
26605
- width: 100%;
26606
- height: 100%;
26607
- background-color: #000;
26608
- display: flex;
26609
- align-items: center;
26610
- justify-content: center;
26611
- }
26612
-
26613
- /* Center the video player and maintain aspect ratio */
26614
- .video-js {
26615
- width: 100%;
26616
- height: 100%;
26617
- max-width: 100%;
26618
- max-height: 100%;
26619
- }
26620
-
26621
- /* Ensure video maintains aspect ratio and shows fully */
26622
- .video-js .vjs-tech {
26623
- position: absolute;
26624
- top: 0;
26625
- left: 0;
26626
- width: 100%;
26627
- height: 100%;
26628
- object-fit: contain;
26629
- }
26630
-
26631
- /* Hide default Video.js loading spinner */
26632
- .video-js .vjs-loading-spinner {
26633
- display: none !important;
26634
- }
26635
-
26636
- /* Custom loading indicator styles */
26637
- .video-player-loading {
26638
- position: absolute;
26639
- top: 50%;
26640
- left: 50%;
26641
- transform: translate(-50%, -50%);
26642
- z-index: 10;
26643
- }
26644
- `;
26645
- if (typeof document !== "undefined") {
26646
- const styleId = "video-player-custom-styles";
26647
- if (!document.getElementById(styleId)) {
26648
- const style = document.createElement("style");
26649
- style.id = styleId;
26650
- style.textContent = videoPlayerStyles;
26651
- document.head.appendChild(style);
26652
- }
26653
- }
26654
- var VideoPlayer = React23__default.forwardRef(({
26655
- src,
26656
- poster,
26657
- autoplay = false,
26658
- controls = true,
26659
- loop = false,
26660
- muted = false,
26661
- playsInline = true,
26662
- className = "",
26663
- options = {},
26664
- externalLoadingControl = false,
26665
- onLoadingChange,
26666
- onReady,
26667
- onPlay,
26668
- onPause,
26669
- onPlaying,
26670
- onTimeUpdate,
26671
- onDurationChange,
26672
- onEnded,
26673
- onError,
26674
- onLoadStart,
26675
- onLoadedMetadata,
26676
- onLoadedData,
26677
- onSeeking,
26678
- onSeeked,
26679
- onClick
26680
- }, ref) => {
26681
- const videoRef = useRef(null);
26682
- const playerRef = useRef(null);
26683
- const [isReady, setIsReady] = useState(false);
26684
- const [isLoading, setIsLoading] = useState(true);
26685
- const [showIndicator, setShowIndicator] = useState(false);
26686
- const [indicatorIsPlaying, setIndicatorIsPlaying] = useState(false);
26687
- const indicatorKeyRef = useRef(0);
26688
- const defaultOptions = {
26689
- controls: false,
26690
- // Always disable Video.js controls - we use custom controls
26691
- // Don't use fluid or fill - let the video maintain its natural aspect ratio
26692
- fluid: false,
26693
- // Disable fluid mode to prevent stretching
26694
- responsive: false,
26695
- // Disable responsive mode
26696
- fill: false,
26697
- // Don't fill - maintain aspect ratio
26698
- playsinline: playsInline,
26699
- preload: "metadata",
26700
- autoplay: autoplay ? "any" : false,
26701
- muted,
26702
- loop,
26703
- poster,
26704
- // Optimized HLS configuration for VOD content with LARGE SEGMENTS (40MB)
26705
- // Strategy: Aggressive buffer reduction for faster startup with large segments
26706
- html5: {
26707
- vhs: {
26708
- // VHS (Video HTTP Streaming) options for HLS
26709
- withCredentials: false,
26710
- handleManifestRedirect: true,
26711
- overrideNative: !videojs.browser.IS_SAFARI,
26712
- // Use native HLS on Safari
26713
- enableLowInitialPlaylist: true,
26714
- smoothQualityChange: true,
26715
- // Optimized bandwidth and buffering for VOD
26716
- bandwidth: 5e7,
26717
- // Start with high bandwidth assumption (50 Mbps)
26718
- initialBandwidth: 5e7,
26719
- // Assume good connection initially
26720
- limitRenditionByPlayerDimensions: true,
26721
- // Buffer configuration optimized for large segments (40MB each)
26722
- maxBufferLength: 3,
26723
- // Start playing with just 3 seconds ahead
26724
- maxMaxBufferLength: 8,
26725
- // Maximum 8 seconds buffer for faster startup
26726
- maxBufferSize: 50 * 1e3 * 1e3,
26727
- // 50MB max buffer size (1.25 segments)
26728
- maxBufferHole: 0.25,
26729
- // Smaller holes for better continuity
26730
- bufferBasedABR: false,
26731
- // Disable for more predictable behavior
26732
- // Segment loading optimization for large segments (40MB)
26733
- maxPlaylistRetries: 3,
26734
- playlistRetryDelay: 500,
26735
- // 500ms between retries
26736
- playlistExclusionDuration: 60,
26737
- segmentLoadingRetryAttempts: 5,
26738
- // More retries for large segments
26739
- segmentLoadingRetryDelay: 500,
26740
- // Faster retry for responsiveness
26741
- segmentLoadingTimeOut: 6e4,
26742
- // 60s timeout for 40MB segments
26743
- manifestLoadingTimeOut: 15e3,
26744
- // 15s timeout for manifest
26745
- // Performance optimizations
26746
- experimentalBufferBasedCodecSwitching: true,
26747
- experimentalCacheEncryptionKeys: true,
26748
- handlePartialData: true,
26749
- allowSeeksWithinUnsafeLiveWindow: false,
26750
- // VOD content
26751
- experimentalLLHLS: false,
26752
- // Disable Low Latency HLS for VOD
26753
- // Connection settings
26754
- // Chrome 130+ started throwing "Cannot perform Construct on a detached ArrayBuffer"
26755
- // whenever the transmux worker tried to rehydrate transferred buffers that originated
26756
- // from Blob-based playlists. Disabling the worker keeps playback stable at the cost
26757
- // of slightly higher main-thread usage, which is acceptable for the dashboard usage.
26758
- enableWorker: false,
26759
- progressive: true,
26760
- // Progressive download
26761
- // Adaptive bitrate settings (if multi-quality available)
26762
- abrEwmaFastLive: 3,
26763
- abrEwmaSlowLive: 9,
26764
- abrBandWidthFactor: 0.95,
26765
- abrBandWidthUpFactor: 0.7,
26766
- abrMaxWithRealBitrate: false,
26767
- // Request options optimized for large segments
26768
- requestOptions: {
26769
- timeout: 9e4,
26770
- // 90s timeout for 40MB segments
26771
- maxRetry: 5
26772
- // More retries for reliability
26773
- }
26774
- },
26775
- nativeVideoTracks: false,
26776
- nativeAudioTracks: false,
26777
- nativeTextTracks: false
26778
- },
26779
- // Improved seeking and scrubbing
26780
- inactivityTimeout: 3e3,
26781
- // Better error handling
26782
- errorDisplay: false,
26783
- // We'll handle errors with callbacks
26784
- // Fullscreen options
26785
- fullscreen: {
26786
- options: {
26787
- navigationUI: "hide"
26788
- }
26789
- },
26790
- ...options
26791
- };
26792
- const initializePlayer = useCallback(() => {
26793
- if (!videoRef.current || playerRef.current) return;
26794
- const videoElement = document.createElement("video-js");
26795
- videoElement.className = "vjs-default-skin";
26796
- videoRef.current.appendChild(videoElement);
26797
- const player = videojs(videoElement, defaultOptions);
26798
- playerRef.current = player;
26799
- player.ready(() => {
26800
- setIsReady(true);
26801
- onReady?.(player);
26802
- });
26803
- player.on("play", () => onPlay?.(player));
26804
- player.on("pause", () => onPause?.(player));
26805
- player.on("playing", () => onPlaying?.(player));
26806
- player.on("timeupdate", () => {
26807
- const currentTime2 = player.currentTime() || 0;
26808
- onTimeUpdate?.(player, currentTime2);
26809
- });
26810
- player.on("durationchange", () => {
26811
- const duration2 = player.duration() || 0;
26812
- onDurationChange?.(player, duration2);
26813
- });
26814
- player.on("ended", () => onEnded?.(player));
26815
- player.on("loadstart", () => {
26540
+ const handleDurationChange = () => {
26541
+ const duration2 = video.duration || 0;
26542
+ eventCallbacksRef.current.onDurationChange?.(player, duration2);
26543
+ };
26544
+ const handleEnded = () => eventCallbacksRef.current.onEnded?.(player);
26545
+ const handleLoadStart = () => {
26816
26546
  setIsLoading(true);
26817
- onLoadingChange?.(true);
26818
- onLoadStart?.(player);
26819
- });
26820
- player.on("loadeddata", () => {
26547
+ eventCallbacksRef.current.onLoadingChange?.(true);
26548
+ eventCallbacksRef.current.onLoadStart?.(player);
26549
+ };
26550
+ const handleLoadedMetadata = () => eventCallbacksRef.current.onLoadedMetadata?.(player);
26551
+ const handleLoadedData = () => {
26821
26552
  setIsLoading(false);
26822
- onLoadingChange?.(false);
26823
- onLoadedData?.(player);
26824
- });
26825
- player.on("waiting", () => {
26553
+ eventCallbacksRef.current.onLoadingChange?.(false);
26554
+ eventCallbacksRef.current.onLoadedData?.(player);
26555
+ };
26556
+ const handleWaiting = () => {
26826
26557
  setIsLoading(true);
26827
- onLoadingChange?.(true);
26828
- });
26829
- player.on("playing", () => {
26830
- setIsLoading(false);
26831
- onLoadingChange?.(false);
26832
- });
26833
- player.on("loadedmetadata", () => {
26834
- onLoadedMetadata?.(player);
26835
- });
26836
- player.on("seeking", () => onSeeking?.(player));
26837
- player.on("seeked", () => onSeeked?.(player));
26838
- player.on("error", () => {
26839
- const error = player.error();
26840
- const errorCode = error?.code ?? 0;
26841
- const errorInfo = ERROR_MAPPING[errorCode] || {
26842
- code: errorCode || 0,
26843
- type: "non_recoverable" /* NON_RECOVERABLE */,
26844
- message: "Unknown playback error occurred",
26845
- canRetry: false
26846
- };
26847
- console.error("[VideoPlayer] Video.js error:", {
26848
- code: errorCode,
26849
- type: errorInfo.type,
26850
- message: errorInfo.message,
26851
- canRetry: errorInfo.canRetry,
26852
- originalError: error
26853
- });
26854
- onError?.(player, errorInfo);
26855
- });
26856
- if (src) {
26857
- const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
26858
- let videoSrc = src;
26859
- let blobUrl = null;
26860
- if (src.startsWith("#EXTM3U")) {
26861
- const safariMode = isSafari();
26862
- const browserName = getBrowserName();
26863
- if (safariMode) {
26864
- const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
26865
- if (clipIdMatch) {
26866
- videoSrc = `/api/clips/playlist/${clipIdMatch[1]}`;
26867
- console.log(`[VideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, videoSrc);
26868
- } else {
26869
- console.warn("[VideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
26870
- const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26871
- blobUrl = URL.createObjectURL(blob);
26872
- videoSrc = blobUrl;
26873
- player._blobUrl = videoSrc;
26874
- }
26875
- } else {
26876
- const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26877
- blobUrl = URL.createObjectURL(blob);
26878
- videoSrc = blobUrl;
26879
- console.log(`[VideoPlayer] Non-Safari browser (${browserName}) - using blob URL for optimal performance`);
26880
- player._blobUrl = videoSrc;
26881
- }
26558
+ eventCallbacksRef.current.onLoadingChange?.(true);
26559
+ };
26560
+ const handleSeeking = () => eventCallbacksRef.current.onSeeking?.(player);
26561
+ const handleSeeked = () => eventCallbacksRef.current.onSeeked?.(player);
26562
+ const handleError = () => {
26563
+ const error = video.error;
26564
+ if (error) {
26565
+ const errorInfo = {
26566
+ code: error.code,
26567
+ type: error.code <= 2 ? "recoverable" : "non_recoverable",
26568
+ message: error.message || "Unknown error occurred",
26569
+ canRetry: error.code <= 2
26570
+ };
26571
+ eventCallbacksRef.current.onError?.(player, errorInfo);
26882
26572
  }
26883
- player.src({
26884
- src: videoSrc,
26885
- type: isHLS ? "application/x-mpegURL" : "video/mp4"
26886
- });
26887
- }
26573
+ };
26574
+ video.addEventListener("canplay", handleCanPlay);
26575
+ video.addEventListener("play", handlePlay);
26576
+ video.addEventListener("pause", handlePause);
26577
+ video.addEventListener("playing", handlePlaying);
26578
+ video.addEventListener("timeupdate", handleTimeUpdate);
26579
+ video.addEventListener("durationchange", handleDurationChange);
26580
+ video.addEventListener("ended", handleEnded);
26581
+ video.addEventListener("loadstart", handleLoadStart);
26582
+ video.addEventListener("loadedmetadata", handleLoadedMetadata);
26583
+ video.addEventListener("loadeddata", handleLoadedData);
26584
+ video.addEventListener("waiting", handleWaiting);
26585
+ video.addEventListener("seeking", handleSeeking);
26586
+ video.addEventListener("seeked", handleSeeked);
26587
+ video.addEventListener("error", handleError);
26588
+ return () => {
26589
+ video.removeEventListener("canplay", handleCanPlay);
26590
+ video.removeEventListener("play", handlePlay);
26591
+ video.removeEventListener("pause", handlePause);
26592
+ video.removeEventListener("playing", handlePlaying);
26593
+ video.removeEventListener("timeupdate", handleTimeUpdate);
26594
+ video.removeEventListener("durationchange", handleDurationChange);
26595
+ video.removeEventListener("ended", handleEnded);
26596
+ video.removeEventListener("loadstart", handleLoadStart);
26597
+ video.removeEventListener("loadedmetadata", handleLoadedMetadata);
26598
+ video.removeEventListener("loadeddata", handleLoadedData);
26599
+ video.removeEventListener("waiting", handleWaiting);
26600
+ video.removeEventListener("seeking", handleSeeking);
26601
+ video.removeEventListener("seeked", handleSeeked);
26602
+ video.removeEventListener("error", handleError);
26603
+ };
26888
26604
  }, [
26889
26605
  src,
26890
- defaultOptions,
26891
- onReady,
26892
- onPlay,
26893
- onPause,
26894
- onTimeUpdate,
26895
- onDurationChange,
26896
- onEnded,
26897
- onError,
26898
- onLoadStart,
26899
- onLoadedMetadata,
26900
- onSeeking,
26901
- onSeeked
26606
+ cleanupBlobUrl,
26607
+ playerLikeObject,
26608
+ configVersion
26902
26609
  ]);
26903
26610
  useEffect(() => {
26904
- if (playerRef.current && src) {
26905
- const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
26906
- let videoSrc = src;
26907
- let blobUrl = null;
26908
- if (src.startsWith("#EXTM3U")) {
26909
- const safariMode = isSafari();
26910
- const browserName = getBrowserName();
26911
- if (safariMode) {
26912
- const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
26913
- if (clipIdMatch) {
26914
- videoSrc = `/api/clips/playlist/${clipIdMatch[1]}`;
26915
- console.log(`[VideoPlayer] Safari detected (${browserName}) - using playlist proxy URL for source update:`, videoSrc);
26916
- } else {
26917
- console.warn("[VideoPlayer] Safari detected but no clip ID found in playlist (source update), trying blob URL fallback");
26918
- const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26919
- blobUrl = URL.createObjectURL(blob);
26920
- videoSrc = blobUrl;
26921
- }
26922
- } else {
26923
- const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
26924
- blobUrl = URL.createObjectURL(blob);
26925
- videoSrc = blobUrl;
26926
- console.log(`[VideoPlayer] Non-Safari browser (${browserName}) - using blob URL for source update`);
26927
- }
26928
- }
26929
- playerRef.current.src({
26930
- src: videoSrc,
26931
- type: isHLS ? "application/x-mpegURL" : "video/mp4"
26932
- });
26933
- return () => {
26934
- if (blobUrl) {
26935
- URL.revokeObjectURL(blobUrl);
26936
- }
26937
- };
26938
- }
26939
- }, [src]);
26940
- useEffect(() => {
26941
- initializePlayer();
26611
+ const cleanup = initializePlayer();
26942
26612
  return () => {
26943
- if (playerRef.current) {
26944
- const blobUrl = playerRef.current._blobUrl;
26945
- if (blobUrl) {
26946
- URL.revokeObjectURL(blobUrl);
26947
- }
26948
- playerRef.current.dispose();
26949
- playerRef.current = null;
26950
- setIsReady(false);
26613
+ cleanup?.();
26614
+ if (hlsRef.current) {
26615
+ hlsRef.current.destroy();
26616
+ hlsRef.current = null;
26951
26617
  }
26618
+ cleanupBlobUrl();
26619
+ setIsReady(false);
26952
26620
  };
26953
- }, []);
26621
+ }, [src, initializePlayer, cleanupBlobUrl]);
26622
+ useEffect(() => {
26623
+ if (videoRef.current) {
26624
+ if (autoplay) {
26625
+ videoRef.current.play().catch((err) => {
26626
+ console.warn("[HlsVideoPlayer] Autoplay failed:", err);
26627
+ });
26628
+ }
26629
+ }
26630
+ }, [autoplay]);
26954
26631
  const play = useCallback(() => {
26955
- return playerRef.current?.play();
26632
+ return videoRef.current?.play();
26956
26633
  }, []);
26957
26634
  const pause = useCallback(() => {
26958
- playerRef.current?.pause();
26635
+ videoRef.current?.pause();
26959
26636
  }, []);
26960
26637
  const currentTime = useCallback((time2) => {
26961
- if (time2 !== void 0) {
26962
- playerRef.current?.currentTime(time2);
26638
+ if (time2 !== void 0 && videoRef.current) {
26639
+ videoRef.current.currentTime = time2;
26963
26640
  return time2;
26964
26641
  }
26965
- return playerRef.current?.currentTime() || 0;
26642
+ return videoRef.current?.currentTime || 0;
26966
26643
  }, []);
26967
26644
  const duration = useCallback(() => {
26968
- return playerRef.current?.duration() || 0;
26645
+ return videoRef.current?.duration || 0;
26969
26646
  }, []);
26970
26647
  const paused = useCallback(() => {
26971
- return playerRef.current?.paused() ?? true;
26648
+ return videoRef.current?.paused ?? true;
26972
26649
  }, []);
26973
26650
  const mute = useCallback((isMuted) => {
26974
- if (isMuted !== void 0) {
26975
- playerRef.current?.muted(isMuted);
26651
+ if (isMuted !== void 0 && videoRef.current) {
26652
+ videoRef.current.muted = isMuted;
26976
26653
  return isMuted;
26977
26654
  }
26978
- return playerRef.current?.muted() ?? false;
26655
+ return videoRef.current?.muted ?? false;
26979
26656
  }, []);
26980
26657
  const volume = useCallback((level) => {
26981
- if (level !== void 0) {
26982
- playerRef.current?.volume(level);
26658
+ if (level !== void 0 && videoRef.current) {
26659
+ videoRef.current.volume = level;
26983
26660
  return level;
26984
26661
  }
26985
- return playerRef.current?.volume() ?? 1;
26662
+ return videoRef.current?.volume ?? 1;
26986
26663
  }, []);
26987
- const dispose = useCallback(() => {
26988
- if (playerRef.current) {
26989
- playerRef.current.dispose();
26990
- playerRef.current = null;
26991
- setIsReady(false);
26664
+ const playbackRate = useCallback((rate) => {
26665
+ if (rate !== void 0 && videoRef.current) {
26666
+ videoRef.current.playbackRate = rate;
26667
+ return rate;
26992
26668
  }
26669
+ return videoRef.current?.playbackRate ?? 1;
26993
26670
  }, []);
26994
- React23__default.useImperativeHandle(ref, () => ({
26995
- player: playerRef.current,
26671
+ useImperativeHandle(ref, () => ({
26672
+ hls: hlsRef.current,
26673
+ video: videoRef.current,
26996
26674
  play,
26997
26675
  pause,
26998
26676
  currentTime,
@@ -27000,13 +26678,15 @@ var VideoPlayer = React23__default.forwardRef(({
27000
26678
  paused,
27001
26679
  mute,
27002
26680
  volume,
26681
+ playbackRate,
27003
26682
  dispose,
27004
- isReady
27005
- }));
26683
+ isReady,
26684
+ // For backward compatibility with Video.js API
26685
+ player: playerLikeObject()
26686
+ }), [play, pause, currentTime, duration, paused, mute, volume, playbackRate, dispose, isReady, playerLikeObject]);
27006
26687
  const handleClickWithIndicator = useCallback(() => {
27007
- if (!onClick || !playerRef.current) return;
27008
- const player = playerRef.current;
27009
- const willBePlaying = player.paused();
26688
+ if (!onClick || !videoRef.current) return;
26689
+ const willBePlaying = videoRef.current.paused;
27010
26690
  setIndicatorIsPlaying(willBePlaying);
27011
26691
  setShowIndicator(false);
27012
26692
  setTimeout(() => {
@@ -27015,17 +26695,30 @@ var VideoPlayer = React23__default.forwardRef(({
27015
26695
  }, 0);
27016
26696
  onClick();
27017
26697
  }, [onClick]);
27018
- return /* @__PURE__ */ jsxs("div", { className: `video-player-wrapper ${className}`, style: { position: "relative", width: "100%", height: "100%" }, children: [
26698
+ return /* @__PURE__ */ jsxs("div", { className: `hls-video-player-wrapper ${className}`, style: { position: "relative", width: "100%", height: "100%" }, children: [
27019
26699
  /* @__PURE__ */ jsx(
27020
26700
  "div",
27021
26701
  {
27022
- className: "video-player-container",
27023
- ref: videoRef,
27024
- "data-vjs-player": true
26702
+ className: "hls-video-player-container",
26703
+ ref: videoContainerRef,
26704
+ children: /* @__PURE__ */ jsx(
26705
+ "video",
26706
+ {
26707
+ ref: videoRef,
26708
+ className: "hls-video-element",
26709
+ poster,
26710
+ controls,
26711
+ loop,
26712
+ muted,
26713
+ playsInline,
26714
+ autoPlay: autoplay,
26715
+ preload: "metadata"
26716
+ }
26717
+ )
27025
26718
  }
27026
26719
  ),
27027
- isLoading && !externalLoadingControl && /* @__PURE__ */ jsx("div", { className: "video-player-loading", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
27028
- onClick && /* @__PURE__ */ jsx(
26720
+ isLoading && !externalLoadingControl && /* @__PURE__ */ jsx("div", { className: "hls-video-player-loading", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
26721
+ onClick && !controls && /* @__PURE__ */ jsx(
27029
26722
  "div",
27030
26723
  {
27031
26724
  onClick: handleClickWithIndicator,
@@ -27041,7 +26734,7 @@ var VideoPlayer = React23__default.forwardRef(({
27041
26734
  "aria-label": "Click to play/pause"
27042
26735
  }
27043
26736
  ),
27044
- onClick && /* @__PURE__ */ jsx(
26737
+ onClick && !controls && /* @__PURE__ */ jsx(
27045
26738
  PlayPauseIndicator,
27046
26739
  {
27047
26740
  show: showIndicator,
@@ -27051,13 +26744,25 @@ var VideoPlayer = React23__default.forwardRef(({
27051
26744
  )
27052
26745
  ] });
27053
26746
  });
27054
- VideoPlayer.displayName = "VideoPlayer";
27055
- var CroppedVideoPlayer = forwardRef(({
26747
+ HlsVideoPlayer.displayName = "HlsVideoPlayer";
26748
+ var VideoPlayer = HlsVideoPlayer;
26749
+ var CroppedHlsVideoPlayer = forwardRef(({
27056
26750
  crop,
27057
26751
  debug = false,
27058
26752
  onClick,
27059
26753
  ...videoProps
27060
26754
  }, ref) => {
26755
+ const {
26756
+ onReady: onReadyProp,
26757
+ onPlay: onPlayProp,
26758
+ onPause: onPauseProp,
26759
+ onEnded: onEndedProp,
26760
+ onSeeking: onSeekingProp,
26761
+ onSeeked: onSeekedProp,
26762
+ onLoadedMetadata: onLoadedMetadataProp,
26763
+ className: inheritedClassName = ""
26764
+ } = videoProps;
26765
+ const videoSrc = videoProps.src;
27061
26766
  const videoContainerRef = useRef(null);
27062
26767
  const hiddenVideoRef = useRef(null);
27063
26768
  const canvasRef = useRef(null);
@@ -27076,8 +26781,11 @@ var CroppedVideoPlayer = forwardRef(({
27076
26781
  }
27077
26782
  }, []);
27078
26783
  useImperativeHandle(ref, () => ({
27079
- get player() {
27080
- return hiddenVideoRef.current?.player || null;
26784
+ get hls() {
26785
+ return hiddenVideoRef.current?.hls || null;
26786
+ },
26787
+ get video() {
26788
+ return hiddenVideoRef.current?.video || null;
27081
26789
  },
27082
26790
  play: () => hiddenVideoRef.current?.play() || void 0,
27083
26791
  pause: () => hiddenVideoRef.current?.pause(),
@@ -27091,12 +26799,36 @@ var CroppedVideoPlayer = forwardRef(({
27091
26799
  paused: () => hiddenVideoRef.current?.paused() || true,
27092
26800
  mute: (isMuted) => hiddenVideoRef.current?.mute(isMuted) || false,
27093
26801
  volume: (level) => hiddenVideoRef.current?.volume(level) || 0,
26802
+ playbackRate: (rate) => hiddenVideoRef.current?.playbackRate(rate) || 1,
27094
26803
  dispose: () => {
27095
26804
  hiddenVideoRef.current?.dispose();
27096
26805
  stopCanvasRendering();
27097
26806
  },
27098
26807
  get isReady() {
27099
26808
  return hiddenVideoRef.current?.isReady || false;
26809
+ },
26810
+ // For backward compatibility with Video.js API
26811
+ get player() {
26812
+ const video = hiddenVideoRef.current?.video;
26813
+ if (!video) return null;
26814
+ return {
26815
+ el: () => video,
26816
+ currentTime: () => video.currentTime || 0,
26817
+ duration: () => video.duration || 0,
26818
+ paused: () => video.paused ?? true,
26819
+ play: () => video.play(),
26820
+ pause: () => video.pause(),
26821
+ muted: (val) => {
26822
+ if (val !== void 0) video.muted = val;
26823
+ return video.muted;
26824
+ },
26825
+ volume: (val) => {
26826
+ if (val !== void 0) video.volume = val;
26827
+ return video.volume;
26828
+ },
26829
+ error: () => null,
26830
+ dispose: () => hiddenVideoRef.current?.dispose()
26831
+ };
27100
26832
  }
27101
26833
  }), [stopCanvasRendering]);
27102
26834
  const calculateCanvasDimensions = useCallback(() => {
@@ -27156,24 +26888,38 @@ var CroppedVideoPlayer = forwardRef(({
27156
26888
  animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
27157
26889
  }, [crop]);
27158
26890
  const handleVideoReady = useCallback((player) => {
27159
- console.log("[CroppedVideoPlayer] Video player ready");
27160
- const videoEl = player.el().querySelector("video");
26891
+ console.log("[CroppedHlsVideoPlayer] Video player ready");
26892
+ const videoEl = hiddenVideoRef.current?.video;
27161
26893
  if (videoEl) {
27162
26894
  videoElementRef.current = videoEl;
27163
26895
  setIsVideoReady(true);
27164
26896
  }
27165
- videoProps.onReady?.(player);
27166
- }, [videoProps]);
26897
+ onReadyProp?.(player);
26898
+ }, [onReadyProp]);
27167
26899
  const handleVideoPlay = useCallback((player) => {
27168
- console.log("[CroppedVideoPlayer] Video playing, starting canvas rendering");
26900
+ console.log("[CroppedHlsVideoPlayer] Video playing, starting canvas rendering");
27169
26901
  if (crop && canvasRef.current) {
27170
26902
  setIsProcessing(true);
27171
26903
  renderFrameToCanvas();
27172
26904
  }
27173
- videoProps.onPlay?.(player);
27174
- }, [crop, renderFrameToCanvas, videoProps]);
26905
+ onPlayProp?.(player);
26906
+ }, [crop, renderFrameToCanvas, onPlayProp]);
27175
26907
  const handleVideoPause = useCallback((player) => {
27176
- console.log("[CroppedVideoPlayer] Video paused, stopping canvas rendering and CLEARING canvas");
26908
+ console.log("[CroppedHlsVideoPlayer] Video paused, stopping canvas rendering and CLEARING canvas");
26909
+ stopCanvasRendering();
26910
+ setIsProcessing(false);
26911
+ if (canvasRef.current) {
26912
+ const ctx = canvasRef.current.getContext("2d");
26913
+ if (ctx) {
26914
+ ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
26915
+ ctx.fillStyle = "black";
26916
+ ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
26917
+ }
26918
+ }
26919
+ onPauseProp?.(player);
26920
+ }, [stopCanvasRendering, onPauseProp]);
26921
+ const handleVideoEnded = useCallback((player) => {
26922
+ console.log("[CroppedHlsVideoPlayer] Video ended, CLEARING canvas");
27177
26923
  stopCanvasRendering();
27178
26924
  setIsProcessing(false);
27179
26925
  if (canvasRef.current) {
@@ -27184,159 +26930,677 @@ var CroppedVideoPlayer = forwardRef(({
27184
26930
  ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
27185
26931
  }
27186
26932
  }
27187
- videoProps.onPause?.(player);
27188
- }, [stopCanvasRendering, videoProps]);
27189
- const handleVideoEnded = useCallback((player) => {
27190
- console.log("[CroppedVideoPlayer] Video ended, CLEARING canvas");
27191
- stopCanvasRendering();
27192
- setIsProcessing(false);
27193
- if (canvasRef.current) {
27194
- const ctx = canvasRef.current.getContext("2d");
27195
- if (ctx) {
27196
- ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
27197
- ctx.fillStyle = "black";
27198
- ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
26933
+ onEndedProp?.(player);
26934
+ }, [stopCanvasRendering, onEndedProp]);
26935
+ const handleSeeking = useCallback((player) => {
26936
+ console.log("[CroppedHlsVideoPlayer] Video seeking");
26937
+ if (crop && !videoElementRef.current?.paused) {
26938
+ renderFrameToCanvas();
26939
+ }
26940
+ onSeekingProp?.(player);
26941
+ }, [crop, renderFrameToCanvas, onSeekingProp]);
26942
+ const handleSeeked = useCallback((player) => {
26943
+ console.log("[CroppedHlsVideoPlayer] Video seeked");
26944
+ if (crop && !videoElementRef.current?.paused) {
26945
+ renderFrameToCanvas();
26946
+ }
26947
+ onSeekedProp?.(player);
26948
+ }, [crop, renderFrameToCanvas, onSeekedProp]);
26949
+ const handleLoadedMetadata = useCallback((player) => {
26950
+ console.log("[CroppedHlsVideoPlayer] Video metadata loaded");
26951
+ calculateCanvasDimensions();
26952
+ onLoadedMetadataProp?.(player);
26953
+ }, [calculateCanvasDimensions, onLoadedMetadataProp]);
26954
+ useEffect(() => {
26955
+ calculateCanvasDimensions();
26956
+ const handleResize = () => {
26957
+ calculateCanvasDimensions();
26958
+ };
26959
+ window.addEventListener("resize", handleResize);
26960
+ return () => {
26961
+ window.removeEventListener("resize", handleResize);
26962
+ };
26963
+ }, [calculateCanvasDimensions]);
26964
+ useLayoutEffect(() => {
26965
+ if (canvasRef.current && crop) {
26966
+ const canvas = canvasRef.current;
26967
+ const ctx = canvas.getContext("2d");
26968
+ if (ctx) {
26969
+ console.log("[CroppedHlsVideoPlayer] Source changing - CLEARING CANVAS IMMEDIATELY");
26970
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
26971
+ ctx.fillStyle = "black";
26972
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
26973
+ }
26974
+ }
26975
+ }, [videoSrc, crop]);
26976
+ useEffect(() => {
26977
+ return () => {
26978
+ stopCanvasRendering();
26979
+ };
26980
+ }, [stopCanvasRendering]);
26981
+ if (!crop) {
26982
+ return /* @__PURE__ */ jsx(HlsVideoPlayer, { ref, ...videoProps, onClick });
26983
+ }
26984
+ const handleClickWithIndicator = () => {
26985
+ if (!onClick || !hiddenVideoRef.current?.video) return;
26986
+ const video = hiddenVideoRef.current.video;
26987
+ const willBePlaying = video.paused;
26988
+ setIndicatorIsPlaying(willBePlaying);
26989
+ setShowIndicator(false);
26990
+ setTimeout(() => {
26991
+ indicatorKeyRef.current += 1;
26992
+ setShowIndicator(true);
26993
+ }, 0);
26994
+ onClick();
26995
+ };
26996
+ return /* @__PURE__ */ jsxs(
26997
+ "div",
26998
+ {
26999
+ ref: videoContainerRef,
27000
+ className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""} ${inheritedClassName}`,
27001
+ onClick: handleClickWithIndicator,
27002
+ children: [
27003
+ /* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx(
27004
+ HlsVideoPlayer,
27005
+ {
27006
+ ref: hiddenVideoRef,
27007
+ ...videoProps,
27008
+ onReady: handleVideoReady,
27009
+ onPlay: handleVideoPlay,
27010
+ onPause: handleVideoPause,
27011
+ onEnded: handleVideoEnded,
27012
+ onSeeking: handleSeeking,
27013
+ onSeeked: handleSeeked,
27014
+ onLoadedMetadata: handleLoadedMetadata,
27015
+ onLoadedData: videoProps.onLoadedData,
27016
+ onPlaying: videoProps.onPlaying,
27017
+ onLoadingChange: videoProps.onLoadingChange
27018
+ }
27019
+ ) }),
27020
+ /* @__PURE__ */ jsx(
27021
+ "canvas",
27022
+ {
27023
+ ref: canvasRef,
27024
+ width: canvasDimensions.width,
27025
+ height: canvasDimensions.height,
27026
+ className: "max-w-full max-h-full",
27027
+ style: {
27028
+ display: isVideoReady ? "block" : "none",
27029
+ width: `${canvasDimensions.width}px`,
27030
+ height: `${canvasDimensions.height}px`
27031
+ }
27032
+ }
27033
+ ),
27034
+ !isVideoReady && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
27035
+ debug && isVideoReady && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
27036
+ /* @__PURE__ */ jsxs("div", { children: [
27037
+ "Crop: ",
27038
+ crop.x,
27039
+ ",",
27040
+ crop.y,
27041
+ " ",
27042
+ crop.width,
27043
+ "x",
27044
+ crop.height,
27045
+ "%"
27046
+ ] }),
27047
+ /* @__PURE__ */ jsxs("div", { children: [
27048
+ "Canvas: ",
27049
+ canvasDimensions.width,
27050
+ "x",
27051
+ canvasDimensions.height,
27052
+ "px"
27053
+ ] }),
27054
+ /* @__PURE__ */ jsxs("div", { children: [
27055
+ "Processing: ",
27056
+ isProcessing ? "Yes" : "No"
27057
+ ] })
27058
+ ] }),
27059
+ onClick && /* @__PURE__ */ jsx(
27060
+ PlayPauseIndicator,
27061
+ {
27062
+ show: showIndicator,
27063
+ isPlaying: indicatorIsPlaying
27064
+ },
27065
+ indicatorKeyRef.current
27066
+ )
27067
+ ]
27068
+ }
27069
+ );
27070
+ });
27071
+ CroppedHlsVideoPlayer.displayName = "CroppedHlsVideoPlayer";
27072
+ var CroppedVideoPlayer = CroppedHlsVideoPlayer;
27073
+ var getSupabaseClient2 = () => {
27074
+ const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
27075
+ const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
27076
+ if (!url || !key) {
27077
+ throw new Error("Supabase configuration missing");
27078
+ }
27079
+ return createClient(url, key);
27080
+ };
27081
+ var getAuthToken3 = async () => {
27082
+ try {
27083
+ const supabase = getSupabaseClient2();
27084
+ const { data: { session } } = await supabase.auth.getSession();
27085
+ return session?.access_token || null;
27086
+ } catch (error) {
27087
+ console.error("[useWorkspaceCrop] Error getting auth token:", error);
27088
+ return null;
27089
+ }
27090
+ };
27091
+ function useWorkspaceCrop(workspaceId) {
27092
+ const [crop, setCrop] = useState(null);
27093
+ const [isLoading, setIsLoading] = useState(true);
27094
+ const [error, setError] = useState(null);
27095
+ useEffect(() => {
27096
+ if (!workspaceId) {
27097
+ setIsLoading(false);
27098
+ return;
27099
+ }
27100
+ const fetchCrop = async () => {
27101
+ setIsLoading(true);
27102
+ setError(null);
27103
+ try {
27104
+ const token = await getAuthToken3();
27105
+ if (!token) {
27106
+ throw new Error("Authentication required");
27107
+ }
27108
+ const response = await fetch("/api/clips/supabase", {
27109
+ method: "POST",
27110
+ headers: {
27111
+ "Content-Type": "application/json",
27112
+ "Authorization": `Bearer ${token}`
27113
+ },
27114
+ body: JSON.stringify({
27115
+ action: "crop",
27116
+ workspaceId
27117
+ })
27118
+ });
27119
+ if (!response.ok) {
27120
+ throw new Error(`Failed to fetch crop: ${response.statusText}`);
27121
+ }
27122
+ const data = await response.json();
27123
+ console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
27124
+ setCrop(data.crop);
27125
+ } catch (err) {
27126
+ console.error("[useWorkspaceCrop] Error fetching crop:", err);
27127
+ setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
27128
+ setCrop(null);
27129
+ } finally {
27130
+ setIsLoading(false);
27131
+ }
27132
+ };
27133
+ fetchCrop();
27134
+ }, [workspaceId]);
27135
+ return { crop, isLoading, error };
27136
+ }
27137
+ function Skeleton({ className, ...props }) {
27138
+ return /* @__PURE__ */ jsx("div", { className: cn("animate-pulse rounded-md bg-muted", className), ...props });
27139
+ }
27140
+ var Select = SelectPrimitive.Root;
27141
+ var SelectGroup = SelectPrimitive.Group;
27142
+ var SelectValue = SelectPrimitive.Value;
27143
+ var SelectTrigger = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
27144
+ SelectPrimitive.Trigger,
27145
+ {
27146
+ ref,
27147
+ className: cn(
27148
+ "flex h-11 sm:h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 touch-manipulation",
27149
+ className
27150
+ ),
27151
+ ...props,
27152
+ children: [
27153
+ children,
27154
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
27155
+ ]
27156
+ }
27157
+ ));
27158
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
27159
+ var SelectScrollUpButton = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27160
+ SelectPrimitive.ScrollUpButton,
27161
+ {
27162
+ ref,
27163
+ className: cn("flex cursor-default items-center justify-center py-1", className),
27164
+ ...props,
27165
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
27166
+ }
27167
+ ));
27168
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
27169
+ var SelectScrollDownButton = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27170
+ SelectPrimitive.ScrollDownButton,
27171
+ {
27172
+ ref,
27173
+ className: cn("flex cursor-default items-center justify-center py-1", className),
27174
+ ...props,
27175
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
27176
+ }
27177
+ ));
27178
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
27179
+ var SelectContent = React23.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
27180
+ SelectPrimitive.Content,
27181
+ {
27182
+ ref,
27183
+ className: cn(
27184
+ "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
27185
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
27186
+ className
27187
+ ),
27188
+ position,
27189
+ ...props,
27190
+ children: [
27191
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
27192
+ /* @__PURE__ */ jsx(
27193
+ SelectPrimitive.Viewport,
27194
+ {
27195
+ className: cn(
27196
+ "p-1",
27197
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
27198
+ ),
27199
+ children
27200
+ }
27201
+ ),
27202
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
27203
+ ]
27204
+ }
27205
+ ) }));
27206
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
27207
+ var SelectLabel = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27208
+ SelectPrimitive.Label,
27209
+ {
27210
+ ref,
27211
+ className: cn("px-2 py-1.5 text-sm font-semibold", className),
27212
+ ...props
27213
+ }
27214
+ ));
27215
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
27216
+ var SelectItem = React23.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
27217
+ SelectPrimitive.Item,
27218
+ {
27219
+ ref,
27220
+ className: cn(
27221
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
27222
+ className
27223
+ ),
27224
+ ...props,
27225
+ children: [
27226
+ /* @__PURE__ */ jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
27227
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
27228
+ ]
27229
+ }
27230
+ ));
27231
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
27232
+ var SelectSeparator = React23.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
27233
+ SelectPrimitive.Separator,
27234
+ {
27235
+ ref,
27236
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
27237
+ ...props
27238
+ }
27239
+ ));
27240
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
27241
+ var LoadingOverlay = ({
27242
+ isVisible,
27243
+ message = "Loading...",
27244
+ className
27245
+ }) => {
27246
+ if (!isVisible) return null;
27247
+ return /* @__PURE__ */ jsx(
27248
+ motion.div,
27249
+ {
27250
+ initial: { opacity: 0 },
27251
+ animate: { opacity: 1 },
27252
+ exit: { opacity: 0 },
27253
+ transition: { duration: 0.2 },
27254
+ className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
27255
+ "aria-modal": "true",
27256
+ role: "dialog",
27257
+ children: /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message }) })
27258
+ }
27259
+ );
27260
+ };
27261
+ var LoadingOverlay_default = LoadingOverlay;
27262
+ var TimeDisplay = ({ className, variant = "default" }) => {
27263
+ const { dateTimeConfig } = useDashboardConfig();
27264
+ const [time2, setTime] = useState("");
27265
+ const dbTimezone = useAppTimezone();
27266
+ const timezoneToDisplay = dbTimezone || dateTimeConfig?.defaultTimezone || "UTC";
27267
+ const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
27268
+ const timeSuffix = "";
27269
+ useEffect(() => {
27270
+ const updateTime = () => {
27271
+ const now2 = /* @__PURE__ */ new Date();
27272
+ const effectiveFormatOptions = {
27273
+ hour: "2-digit",
27274
+ minute: "2-digit",
27275
+ second: "2-digit",
27276
+ hour12: true,
27277
+ timeZone: timezoneToDisplay,
27278
+ ...dateTimeConfig?.timeFormatOptions || {}
27279
+ // Allow override from config
27280
+ };
27281
+ try {
27282
+ setTime(new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2));
27283
+ } catch (e) {
27284
+ console.error("Error formatting time:", e);
27285
+ setTime("Error");
27286
+ }
27287
+ };
27288
+ updateTime();
27289
+ const interval = setInterval(updateTime, 1e3);
27290
+ return () => clearInterval(interval);
27291
+ }, [timezoneToDisplay, dateTimeConfig?.timeFormatOptions, localeToUse]);
27292
+ if (!time2) return null;
27293
+ if (variant === "minimal") {
27294
+ return /* @__PURE__ */ jsxs("span", { className: className || "", children: [
27295
+ time2,
27296
+ " ",
27297
+ timeSuffix
27298
+ ] });
27299
+ }
27300
+ return /* @__PURE__ */ jsxs(
27301
+ motion.div,
27302
+ {
27303
+ initial: { opacity: 0, y: -5 },
27304
+ animate: { opacity: 1, y: 0 },
27305
+ transition: { duration: 0.3 },
27306
+ className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
27307
+ children: [
27308
+ /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-[var(--primary-DEFAULT)]", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z", clipRule: "evenodd" }) }),
27309
+ /* @__PURE__ */ jsxs("span", { className: "text-xs sm:text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
27310
+ time2,
27311
+ " ",
27312
+ timeSuffix
27313
+ ] })
27314
+ ]
27315
+ }
27316
+ );
27317
+ };
27318
+ var TimeDisplay_default = TimeDisplay;
27319
+ var DateDisplay = ({ className, variant = "default" }) => {
27320
+ const { dateTimeConfig } = useDashboardConfig();
27321
+ const [date, setDate] = useState("");
27322
+ const timezoneToDisplay = dateTimeConfig?.defaultTimezone || "UTC";
27323
+ const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
27324
+ useEffect(() => {
27325
+ const getCurrentFormattedDate = () => {
27326
+ const now2 = /* @__PURE__ */ new Date();
27327
+ const effectiveFormatOptions = {
27328
+ weekday: "short",
27329
+ day: "numeric",
27330
+ month: "short",
27331
+ timeZone: timezoneToDisplay,
27332
+ ...dateTimeConfig?.dateFormatOptions || {}
27333
+ // Allow override from config
27334
+ };
27335
+ try {
27336
+ return new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2);
27337
+ } catch (e) {
27338
+ console.error("Error formatting date:", e);
27339
+ return "Error";
27340
+ }
27341
+ };
27342
+ const updateDate = () => {
27343
+ setDate(getCurrentFormattedDate());
27344
+ };
27345
+ updateDate();
27346
+ const interval = setInterval(() => {
27347
+ const currentDateStr = getCurrentFormattedDate();
27348
+ if (currentDateStr !== date) {
27349
+ updateDate();
27350
+ }
27351
+ }, 60 * 1e3);
27352
+ return () => clearInterval(interval);
27353
+ }, [date, timezoneToDisplay, dateTimeConfig?.dateFormatOptions, localeToUse]);
27354
+ if (!date) return null;
27355
+ if (variant === "minimal") {
27356
+ return /* @__PURE__ */ jsx("span", { className: className || "", children: date });
27357
+ }
27358
+ return /* @__PURE__ */ jsxs(
27359
+ motion.div,
27360
+ {
27361
+ initial: { opacity: 0, y: -5 },
27362
+ animate: { opacity: 1, y: 0 },
27363
+ transition: { duration: 0.3 },
27364
+ className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
27365
+ children: [
27366
+ /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-3 w-3 text-blue-600", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z", clipRule: "evenodd" }) }),
27367
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-gray-800 tracking-tight", children: date })
27368
+ ]
27369
+ }
27370
+ );
27371
+ };
27372
+ var DateDisplay_default = DateDisplay;
27373
+ var Card3 = Card2;
27374
+ var CardHeader3 = CardHeader2;
27375
+ var CardTitle3 = CardTitle2;
27376
+ var CardContent3 = CardContent2;
27377
+ var MetricCard2 = ({ title, value, unit = "", trend = null }) => {
27378
+ const getTrendColor = (trendValue) => {
27379
+ if (trendValue === null || trendValue === void 0) return "";
27380
+ return trendValue > 0 ? "text-green-500" : trendValue < 0 ? "text-red-500" : "text-gray-500";
27381
+ };
27382
+ const getTrendSymbol = (trendValue) => {
27383
+ if (trendValue === null || trendValue === void 0) return "";
27384
+ return trendValue > 0 ? "\u2191" : trendValue < 0 ? "\u2193" : "";
27385
+ };
27386
+ return /* @__PURE__ */ jsxs(Card3, { className: "bg-white", children: [
27387
+ /* @__PURE__ */ jsx(CardHeader3, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle3, { className: "text-sm font-medium text-gray-500", children: title }) }),
27388
+ /* @__PURE__ */ jsx(CardContent3, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
27389
+ /* @__PURE__ */ jsxs("div", { className: "text-2xl font-semibold", children: [
27390
+ value,
27391
+ unit && /* @__PURE__ */ jsx("span", { className: "ml-1 text-sm text-gray-500", children: unit })
27392
+ ] }),
27393
+ trend !== null && trend !== void 0 && // Check trend for null/undefined before accessing
27394
+ /* @__PURE__ */ jsx("div", { className: `flex items-center ${getTrendColor(trend)}`, children: /* @__PURE__ */ jsxs("span", { className: "text-sm", children: [
27395
+ getTrendSymbol(trend),
27396
+ " ",
27397
+ Math.abs(trend),
27398
+ "%"
27399
+ ] }) })
27400
+ ] }) })
27401
+ ] });
27402
+ };
27403
+ var MetricCard_default = MetricCard2;
27404
+ var TimePickerDropdown = ({
27405
+ value,
27406
+ onChange,
27407
+ placeholder = "Select time",
27408
+ className = "",
27409
+ disabled = false
27410
+ }) => {
27411
+ const [isOpen, setIsOpen] = useState(false);
27412
+ const [searchTerm, setSearchTerm] = useState("");
27413
+ const dropdownRef = useRef(null);
27414
+ const inputRef = useRef(null);
27415
+ const generateTimeSlots = () => {
27416
+ const slots = [];
27417
+ for (let hour = 0; hour < 24; hour++) {
27418
+ for (let minute = 0; minute < 60; minute += 15) {
27419
+ const time24 = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
27420
+ const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
27421
+ const ampm = hour < 12 ? "AM" : "PM";
27422
+ const time12 = `${hour12}:${minute.toString().padStart(2, "0")} ${ampm}`;
27423
+ slots.push({ value: time24, label: time12 });
27424
+ }
27425
+ }
27426
+ return slots;
27427
+ };
27428
+ const timeSlots = generateTimeSlots();
27429
+ const filteredSlots = timeSlots.filter(
27430
+ (slot) => slot.label.toLowerCase().includes(searchTerm.toLowerCase())
27431
+ );
27432
+ const getDisplayValue = (value2) => {
27433
+ if (!value2) return "";
27434
+ const normalizedValue = value2.substring(0, 5);
27435
+ const slot = timeSlots.find((s) => s.value === normalizedValue);
27436
+ return slot ? slot.label : value2;
27437
+ };
27438
+ useEffect(() => {
27439
+ const handleClickOutside = (event) => {
27440
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
27441
+ setIsOpen(false);
27442
+ setSearchTerm("");
27443
+ }
27444
+ };
27445
+ document.addEventListener("mousedown", handleClickOutside);
27446
+ return () => document.removeEventListener("mousedown", handleClickOutside);
27447
+ }, []);
27448
+ const handleKeyDown = (e) => {
27449
+ if (e.key === "Escape") {
27450
+ setIsOpen(false);
27451
+ setSearchTerm("");
27452
+ } else if (e.key === "Enter") {
27453
+ e.preventDefault();
27454
+ if (filteredSlots.length > 0) {
27455
+ onChange(filteredSlots[0].value);
27456
+ setIsOpen(false);
27457
+ setSearchTerm("");
27199
27458
  }
27200
27459
  }
27201
- videoProps.onEnded?.(player);
27202
- }, [stopCanvasRendering, videoProps]);
27203
- const handleSeeking = useCallback((player) => {
27204
- console.log("[CroppedVideoPlayer] Video seeking");
27205
- if (crop && !player.paused()) {
27206
- renderFrameToCanvas();
27207
- }
27208
- videoProps.onSeeking?.(player);
27209
- }, [crop, renderFrameToCanvas, videoProps]);
27210
- const handleSeeked = useCallback((player) => {
27211
- console.log("[CroppedVideoPlayer] Video seeked");
27212
- if (crop && !player.paused()) {
27213
- renderFrameToCanvas();
27460
+ };
27461
+ const handleSelect = (timeValue) => {
27462
+ onChange(timeValue);
27463
+ setIsOpen(false);
27464
+ setSearchTerm("");
27465
+ };
27466
+ const handleToggle = () => {
27467
+ if (disabled) return;
27468
+ setIsOpen(!isOpen);
27469
+ if (!isOpen) {
27470
+ setTimeout(() => inputRef.current?.focus(), 100);
27214
27471
  }
27215
- videoProps.onSeeked?.(player);
27216
- }, [crop, renderFrameToCanvas, videoProps]);
27217
- const handleLoadedMetadata = useCallback((player) => {
27218
- console.log("[CroppedVideoPlayer] Video metadata loaded");
27219
- calculateCanvasDimensions();
27220
- videoProps.onLoadedMetadata?.(player);
27221
- }, [calculateCanvasDimensions, videoProps]);
27222
- useEffect(() => {
27223
- calculateCanvasDimensions();
27224
- const handleResize = () => {
27225
- calculateCanvasDimensions();
27472
+ };
27473
+ return /* @__PURE__ */ jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [
27474
+ /* @__PURE__ */ jsx(
27475
+ "button",
27476
+ {
27477
+ type: "button",
27478
+ onClick: handleToggle,
27479
+ disabled,
27480
+ className: `
27481
+ w-full px-3 py-2 text-left bg-white border border-gray-300 rounded-md shadow-sm
27482
+ focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
27483
+ hover:border-gray-400 transition-colors duration-200
27484
+ ${disabled ? "bg-gray-50 cursor-not-allowed" : "cursor-pointer"}
27485
+ ${isOpen ? "ring-2 ring-blue-500 border-blue-500" : ""}
27486
+ `,
27487
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
27488
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
27489
+ /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-gray-400" }),
27490
+ /* @__PURE__ */ jsx("span", { className: `text-sm ${value ? "text-gray-900" : "text-gray-500"}`, children: value ? getDisplayValue(value) : placeholder })
27491
+ ] }),
27492
+ /* @__PURE__ */ jsx(
27493
+ ChevronDown,
27494
+ {
27495
+ className: `h-4 w-4 text-gray-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
27496
+ }
27497
+ )
27498
+ ] })
27499
+ }
27500
+ ),
27501
+ isOpen && /* @__PURE__ */ jsxs("div", { className: "absolute z-50 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-hidden", children: [
27502
+ /* @__PURE__ */ jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsx(
27503
+ "input",
27504
+ {
27505
+ ref: inputRef,
27506
+ type: "text",
27507
+ placeholder: "Search time...",
27508
+ value: searchTerm,
27509
+ onChange: (e) => setSearchTerm(e.target.value),
27510
+ onKeyDown: handleKeyDown,
27511
+ className: "w-full px-3 py-2 text-sm border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
27512
+ }
27513
+ ) }),
27514
+ /* @__PURE__ */ jsx("div", { className: "overflow-y-auto max-h-48", children: filteredSlots.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: "No times found" }) : filteredSlots.map((slot) => /* @__PURE__ */ jsx(
27515
+ "button",
27516
+ {
27517
+ type: "button",
27518
+ onClick: () => handleSelect(slot.value),
27519
+ className: `
27520
+ w-full px-3 py-2 text-left text-sm hover:bg-blue-50 hover:text-blue-600
27521
+ transition-colors duration-150 border-b border-gray-100 last:border-b-0
27522
+ ${slot.value === value ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-700"}
27523
+ `,
27524
+ children: slot.label
27525
+ },
27526
+ slot.value
27527
+ )) })
27528
+ ] })
27529
+ ] });
27530
+ };
27531
+ var SilentErrorBoundary = class extends React23__default.Component {
27532
+ constructor(props) {
27533
+ super(props);
27534
+ this.handleClearAndReload = () => {
27535
+ console.log("[ErrorBoundary] User initiated reset");
27536
+ if (typeof window !== "undefined") {
27537
+ try {
27538
+ localStorage.clear();
27539
+ sessionStorage.clear();
27540
+ console.log("[ErrorBoundary] Cleared all storage");
27541
+ } catch (error) {
27542
+ console.error("[ErrorBoundary] Failed to clear storage:", error);
27543
+ }
27544
+ }
27545
+ window.location.href = "/login";
27226
27546
  };
27227
- window.addEventListener("resize", handleResize);
27228
- return () => {
27229
- window.removeEventListener("resize", handleResize);
27547
+ this.state = {
27548
+ hasError: false,
27549
+ errorCount: 0,
27550
+ lastError: null,
27551
+ errorInfo: null
27230
27552
  };
27231
- }, [calculateCanvasDimensions]);
27232
- useLayoutEffect(() => {
27233
- if (canvasRef.current && crop) {
27234
- const canvas = canvasRef.current;
27235
- const ctx = canvas.getContext("2d");
27236
- if (ctx) {
27237
- console.log("[CroppedVideoPlayer] Source changing - CLEARING CANVAS IMMEDIATELY");
27238
- ctx.clearRect(0, 0, canvas.width, canvas.height);
27239
- ctx.fillStyle = "black";
27240
- ctx.fillRect(0, 0, canvas.width, canvas.height);
27553
+ }
27554
+ static getDerivedStateFromError(error) {
27555
+ return { hasError: true };
27556
+ }
27557
+ componentDidCatch(error, errorInfo) {
27558
+ console.error("[ErrorBoundary] Caught render error:", {
27559
+ error: error.message,
27560
+ stack: error.stack,
27561
+ componentStack: errorInfo.componentStack,
27562
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
27563
+ });
27564
+ this.setState((prev) => ({
27565
+ errorCount: prev.errorCount + 1,
27566
+ lastError: error,
27567
+ errorInfo
27568
+ }));
27569
+ try {
27570
+ if (typeof window !== "undefined" && window.mixpanel) {
27571
+ window.mixpanel.track("React Render Error", {
27572
+ error: error.message,
27573
+ component: errorInfo.componentStack?.split("\n")[1] || "unknown",
27574
+ errorCount: this.state.errorCount + 1
27575
+ });
27241
27576
  }
27577
+ } catch (analyticsError) {
27578
+ console.warn("[ErrorBoundary] Analytics tracking failed:", analyticsError);
27242
27579
  }
27243
- }, [videoProps.src, crop]);
27244
- useEffect(() => {
27245
- return () => {
27246
- stopCanvasRendering();
27247
- };
27248
- }, [stopCanvasRendering]);
27249
- if (!crop) {
27250
- return /* @__PURE__ */ jsx(VideoPlayer, { ref, ...videoProps, onClick });
27251
27580
  }
27252
- const handleClickWithIndicator = () => {
27253
- if (!onClick || !hiddenVideoRef.current?.player) return;
27254
- const player = hiddenVideoRef.current.player;
27255
- const willBePlaying = player.paused();
27256
- setIndicatorIsPlaying(willBePlaying);
27257
- setShowIndicator(false);
27258
- setTimeout(() => {
27259
- indicatorKeyRef.current += 1;
27260
- setShowIndicator(true);
27261
- }, 0);
27262
- onClick();
27263
- };
27264
- return /* @__PURE__ */ jsxs(
27265
- "div",
27266
- {
27267
- ref: videoContainerRef,
27268
- className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""} ${videoProps.className || ""}`,
27269
- onClick: handleClickWithIndicator,
27270
- children: [
27271
- /* @__PURE__ */ jsx("div", { className: "hidden", children: /* @__PURE__ */ jsx(
27272
- VideoPlayer,
27273
- {
27274
- ref: hiddenVideoRef,
27275
- ...videoProps,
27276
- onReady: handleVideoReady,
27277
- onPlay: handleVideoPlay,
27278
- onPause: handleVideoPause,
27279
- onEnded: handleVideoEnded,
27280
- onSeeking: handleSeeking,
27281
- onSeeked: handleSeeked,
27282
- onLoadedMetadata: handleLoadedMetadata,
27283
- onLoadedData: videoProps.onLoadedData,
27284
- onPlaying: videoProps.onPlaying,
27285
- onLoadingChange: videoProps.onLoadingChange
27286
- }
27287
- ) }),
27288
- /* @__PURE__ */ jsx(
27289
- "canvas",
27290
- {
27291
- ref: canvasRef,
27292
- width: canvasDimensions.width,
27293
- height: canvasDimensions.height,
27294
- className: "max-w-full max-h-full",
27295
- style: {
27296
- display: isVideoReady ? "block" : "none",
27297
- width: `${canvasDimensions.width}px`,
27298
- height: `${canvasDimensions.height}px`
27299
- }
27300
- }
27301
- ),
27302
- !isVideoReady && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
27303
- debug && isVideoReady && /* @__PURE__ */ jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
27304
- /* @__PURE__ */ jsxs("div", { children: [
27305
- "Crop: ",
27306
- crop.x,
27307
- ",",
27308
- crop.y,
27309
- " ",
27310
- crop.width,
27311
- "x",
27312
- crop.height,
27313
- "%"
27314
- ] }),
27315
- /* @__PURE__ */ jsxs("div", { children: [
27316
- "Canvas: ",
27317
- canvasDimensions.width,
27318
- "x",
27319
- canvasDimensions.height,
27320
- "px"
27321
- ] }),
27322
- /* @__PURE__ */ jsxs("div", { children: [
27323
- "Processing: ",
27324
- isProcessing ? "Yes" : "No"
27325
- ] })
27326
- ] }),
27327
- onClick && /* @__PURE__ */ jsx(
27328
- PlayPauseIndicator,
27329
- {
27330
- show: showIndicator,
27331
- isPlaying: indicatorIsPlaying
27332
- },
27333
- indicatorKeyRef.current
27334
- )
27335
- ]
27581
+ render() {
27582
+ if (!this.state.hasError) {
27583
+ return this.props.children;
27336
27584
  }
27337
- );
27338
- });
27339
- CroppedVideoPlayer.displayName = "CroppedVideoPlayer";
27585
+ return /* @__PURE__ */ jsx("div", { className: "flex h-screen w-screen items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center space-y-6 text-center", children: [
27586
+ /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading Dashboard..." }),
27587
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Taking longer than usual..." }),
27588
+ /* @__PURE__ */ jsx("div", { className: "pt-4", children: /* @__PURE__ */ jsx(
27589
+ "a",
27590
+ {
27591
+ href: "#",
27592
+ onClick: (e) => {
27593
+ e.preventDefault();
27594
+ this.handleClearAndReload();
27595
+ },
27596
+ className: "text-xs text-gray-400 hover:text-gray-600 underline transition-colors",
27597
+ children: "Reset and try again"
27598
+ }
27599
+ ) }),
27600
+ process.env.NODE_ENV === "development" && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-400 italic mt-4", children: "Check console for error details" })
27601
+ ] }) });
27602
+ }
27603
+ };
27340
27604
  var BackButton = ({
27341
27605
  onClick,
27342
27606
  text = "Back",
@@ -27701,70 +27965,6 @@ var NewClipsNotification = ({
27701
27965
  }
27702
27966
  );
27703
27967
  };
27704
- var getSupabaseClient2 = () => {
27705
- const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
27706
- const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
27707
- if (!url || !key) {
27708
- throw new Error("Supabase configuration missing");
27709
- }
27710
- return createClient(url, key);
27711
- };
27712
- var getAuthToken3 = async () => {
27713
- try {
27714
- const supabase = getSupabaseClient2();
27715
- const { data: { session } } = await supabase.auth.getSession();
27716
- return session?.access_token || null;
27717
- } catch (error) {
27718
- console.error("[useWorkspaceCrop] Error getting auth token:", error);
27719
- return null;
27720
- }
27721
- };
27722
- function useWorkspaceCrop(workspaceId) {
27723
- const [crop, setCrop] = useState(null);
27724
- const [isLoading, setIsLoading] = useState(true);
27725
- const [error, setError] = useState(null);
27726
- useEffect(() => {
27727
- if (!workspaceId) {
27728
- setIsLoading(false);
27729
- return;
27730
- }
27731
- const fetchCrop = async () => {
27732
- setIsLoading(true);
27733
- setError(null);
27734
- try {
27735
- const token = await getAuthToken3();
27736
- if (!token) {
27737
- throw new Error("Authentication required");
27738
- }
27739
- const response = await fetch("/api/clips/supabase", {
27740
- method: "POST",
27741
- headers: {
27742
- "Content-Type": "application/json",
27743
- "Authorization": `Bearer ${token}`
27744
- },
27745
- body: JSON.stringify({
27746
- action: "crop",
27747
- workspaceId
27748
- })
27749
- });
27750
- if (!response.ok) {
27751
- throw new Error(`Failed to fetch crop: ${response.statusText}`);
27752
- }
27753
- const data = await response.json();
27754
- console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
27755
- setCrop(data.crop);
27756
- } catch (err) {
27757
- console.error("[useWorkspaceCrop] Error fetching crop:", err);
27758
- setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
27759
- setCrop(null);
27760
- } finally {
27761
- setIsLoading(false);
27762
- }
27763
- };
27764
- fetchCrop();
27765
- }, [workspaceId]);
27766
- return { crop, isLoading, error };
27767
- }
27768
27968
  var parseCycleTime = (value) => {
27769
27969
  if (typeof value === "number" && Number.isFinite(value)) {
27770
27970
  return value;
@@ -29020,6 +29220,11 @@ var BottlenecksContent = ({
29020
29220
  return Number.isFinite(numericValue) ? numericValue : null;
29021
29221
  })();
29022
29222
  const videoRef = useRef(null);
29223
+ const videoPlayerOptions = useMemo(() => ({
29224
+ fluid: false,
29225
+ responsive: false,
29226
+ fill: false
29227
+ }), []);
29023
29228
  const [initialFilter, setInitialFilter] = useState("");
29024
29229
  const currentIndexRef = useRef(0);
29025
29230
  const activeFilterRef = useRef(initialFilter);
@@ -29030,6 +29235,7 @@ var BottlenecksContent = ({
29030
29235
  const [duration, setDuration] = useState(0);
29031
29236
  const [currentIndex, setCurrentIndex] = useState(0);
29032
29237
  const [currentClipId, setCurrentClipId] = useState(null);
29238
+ const [playbackSpeed, setPlaybackSpeed] = useState(1);
29033
29239
  const [isTransitioning, setIsTransitioning] = useState(false);
29034
29240
  const [pendingVideo, setPendingVideo] = useState(null);
29035
29241
  const [isVideoBuffering, setIsVideoBuffering] = useState(false);
@@ -29047,6 +29253,31 @@ var BottlenecksContent = ({
29047
29253
  const [categoryMetadata, setCategoryMetadata] = useState([]);
29048
29254
  const [currentMetadataIndex, setCurrentMetadataIndex] = useState(0);
29049
29255
  const [metadataCache, setMetadataCache] = useState({});
29256
+ const invalidateMetadataCache = useCallback((categories) => {
29257
+ setMetadataCache((prevCache) => {
29258
+ if (!prevCache || Object.keys(prevCache).length === 0) {
29259
+ return prevCache;
29260
+ }
29261
+ const targetCategories = categories ? (Array.isArray(categories) ? categories : [categories]).filter(Boolean) : null;
29262
+ let updatedCache = null;
29263
+ const shouldInvalidate = (key) => {
29264
+ if (!targetCategories || targetCategories.length === 0) {
29265
+ return true;
29266
+ }
29267
+ const [categoryId] = key.split("-");
29268
+ return targetCategories.includes(categoryId);
29269
+ };
29270
+ Object.keys(prevCache).forEach((cacheKey) => {
29271
+ if (shouldInvalidate(cacheKey)) {
29272
+ if (!updatedCache) {
29273
+ updatedCache = { ...prevCache };
29274
+ }
29275
+ delete updatedCache[cacheKey];
29276
+ }
29277
+ });
29278
+ return updatedCache || prevCache;
29279
+ });
29280
+ }, []);
29050
29281
  const [triageClips, setTriageClips] = useState([]);
29051
29282
  const [isLoadingTriageClips, setIsLoadingTriageClips] = useState(false);
29052
29283
  const [isFullscreen, setIsFullscreen] = useState(false);
@@ -29066,6 +29297,12 @@ var BottlenecksContent = ({
29066
29297
  onNewClips: (notification) => {
29067
29298
  console.log(`[BottlenecksContent] New clips detected:`, notification);
29068
29299
  if (notification.clips.length > 0) {
29300
+ const categoryIds = notification.clips.map((clip) => clip.clip_type).filter(Boolean).map((value) => String(value));
29301
+ if (categoryIds.length > 0) {
29302
+ invalidateMetadataCache(categoryIds);
29303
+ } else {
29304
+ invalidateMetadataCache();
29305
+ }
29069
29306
  fetchClipCounts();
29070
29307
  }
29071
29308
  }
@@ -29112,24 +29349,37 @@ var BottlenecksContent = ({
29112
29349
  shift: shift || "0"
29113
29350
  });
29114
29351
  useEffect(() => {
29115
- if (clipTypes.length > 0 && !initialFilter) {
29116
- const priorityOrder = ["cycle_completion", "fast-cycles", "slow-cycles", "idle_time"];
29117
- let selectedType = null;
29118
- for (const priorityType of priorityOrder) {
29119
- const type = clipTypes.find((t) => t.type === priorityType && (dynamicCounts[t.type] || 0) > 0);
29120
- if (type) {
29121
- selectedType = type;
29122
- break;
29352
+ if (clipTypes.length > 0) {
29353
+ const currentFilterCount = initialFilter ? dynamicCounts[initialFilter] || 0 : 0;
29354
+ const hasAnyCounts = Object.values(dynamicCounts).some((c) => c > 0);
29355
+ const userHasNotNavigated = !initialFilter || activeFilterRef.current === initialFilter;
29356
+ const shouldRunSelection = !initialFilter || userHasNotNavigated && currentFilterCount === 0 && hasAnyCounts;
29357
+ if (shouldRunSelection) {
29358
+ let selectedType = null;
29359
+ if (clipTypes.length === 1) {
29360
+ selectedType = clipTypes[0];
29361
+ } else {
29362
+ const priorityOrder = ["cycle_completion", "fast-cycles", "slow-cycles", "idle_time"];
29363
+ for (const priorityType of priorityOrder) {
29364
+ const type = clipTypes.find((t) => t.type === priorityType && (dynamicCounts[t.type] || 0) > 0);
29365
+ if (type) {
29366
+ selectedType = type;
29367
+ break;
29368
+ }
29369
+ }
29370
+ if (!selectedType) {
29371
+ selectedType = clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0);
29372
+ }
29373
+ if (!selectedType) {
29374
+ selectedType = clipTypes[0];
29375
+ }
29376
+ }
29377
+ if (selectedType && selectedType.type !== initialFilter) {
29378
+ console.log(`[BottlenecksContent] Auto-selecting filter: ${selectedType.type} (count: ${dynamicCounts[selectedType.type] || 0})`);
29379
+ setInitialFilter(selectedType.type);
29380
+ setActiveFilter(selectedType.type);
29381
+ activeFilterRef.current = selectedType.type;
29123
29382
  }
29124
- }
29125
- if (!selectedType) {
29126
- selectedType = clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0);
29127
- }
29128
- const firstType = selectedType || clipTypes[0];
29129
- if (firstType) {
29130
- setInitialFilter(firstType.type);
29131
- setActiveFilter(firstType.type);
29132
- activeFilterRef.current = firstType.type;
29133
29383
  }
29134
29384
  }
29135
29385
  }, [clipTypes, dynamicCounts, initialFilter]);
@@ -29181,7 +29431,7 @@ var BottlenecksContent = ({
29181
29431
  } finally {
29182
29432
  fetchInProgressRef.current.delete(operationKey);
29183
29433
  }
29184
- }, [workspaceId, date, s3ClipsService, effectiveShift, dashboardConfig, updateClipCounts]);
29434
+ }, [workspaceId, date, s3ClipsService, effectiveShift, dashboardConfig, updateClipCounts, timezone, totalOutput]);
29185
29435
  const loadingCategoryRef = useRef(null);
29186
29436
  const loadFirstVideoForCategory = useCallback(async (category) => {
29187
29437
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
@@ -29271,15 +29521,16 @@ var BottlenecksContent = ({
29271
29521
  loadingCategoryRef.current = null;
29272
29522
  fetchInProgressRef.current.delete(operationKey);
29273
29523
  }
29274
- }, [workspaceId, date, s3ClipsService, mergedCounts, effectiveShift]);
29524
+ }, [workspaceId, date, s3ClipsService, mergedCounts, effectiveShift, timezone]);
29275
29525
  const handleRefreshClips = useCallback(async () => {
29276
29526
  console.log("[BottlenecksContent] Refreshing clips after new additions");
29277
29527
  acknowledgeNewClips();
29528
+ invalidateMetadataCache();
29278
29529
  await fetchClipCounts();
29279
29530
  if (activeFilter && mergedCounts[activeFilter] > 0) {
29280
29531
  await loadFirstVideoForCategory(activeFilter);
29281
29532
  }
29282
- }, [acknowledgeNewClips, fetchClipCounts, activeFilter, mergedCounts, loadFirstVideoForCategory]);
29533
+ }, [acknowledgeNewClips, fetchClipCounts, activeFilter, mergedCounts, loadFirstVideoForCategory, invalidateMetadataCache]);
29283
29534
  useEffect(() => {
29284
29535
  if (s3ClipsService) {
29285
29536
  fetchClipCounts();
@@ -29449,38 +29700,38 @@ var BottlenecksContent = ({
29449
29700
  loadingTimeoutRef.current = null;
29450
29701
  }
29451
29702
  }, []);
29452
- const loadCategoryMetadata = useCallback(async (categoryId, autoLoadFirstVideo = false) => {
29703
+ const loadCategoryMetadata = useCallback(async (categoryId, autoLoadFirstVideo = false, forceRefresh = false) => {
29453
29704
  if (!workspaceId) {
29454
29705
  return;
29455
29706
  }
29456
- const cacheKey = `${categoryId}-${date || getOperationalDate(timezone)}-${effectiveShift}`;
29457
- if (metadataCache[cacheKey]) {
29458
- console.log(`[BottlenecksContent] Using cached metadata for ${categoryId}`);
29459
- setCategoryMetadata(metadataCache[cacheKey]);
29460
- categoryMetadataRef.current = metadataCache[cacheKey];
29461
- if (autoLoadFirstVideo && metadataCache[cacheKey].length > 0 && s3ClipsService) {
29462
- const firstClipMeta = metadataCache[cacheKey][0];
29463
- try {
29464
- const video = await s3ClipsService.getClipById(firstClipMeta.clipId);
29465
- if (video && isMountedRef.current) {
29466
- setCurrentClipId(firstClipMeta.clipId);
29467
- setAllVideos([video]);
29468
- setCurrentIndex(0);
29469
- setCurrentMetadataIndex(0);
29470
- currentMetadataIndexRef.current = 0;
29471
- setIsCategoryLoading(false);
29472
- console.log(`[BottlenecksContent] Auto-loaded first video from cache: ${video.id} (1/${metadataCache[cacheKey].length})`);
29707
+ const resolvedDate = date || getOperationalDate(timezone);
29708
+ const cacheKey = `${categoryId}-${resolvedDate}-${effectiveShift}`;
29709
+ const cachedMetadata = !forceRefresh ? metadataCache[cacheKey] : void 0;
29710
+ try {
29711
+ if (cachedMetadata) {
29712
+ console.log(`[BottlenecksContent] Using cached metadata for ${categoryId}`);
29713
+ setCategoryMetadata(cachedMetadata);
29714
+ categoryMetadataRef.current = cachedMetadata;
29715
+ if (autoLoadFirstVideo && cachedMetadata.length > 0 && s3ClipsService) {
29716
+ const firstClipMeta = cachedMetadata[0];
29717
+ try {
29718
+ const video = await s3ClipsService.getClipById(firstClipMeta.clipId);
29719
+ if (video && isMountedRef.current) {
29720
+ setCurrentClipId(firstClipMeta.clipId);
29721
+ setAllVideos([video]);
29722
+ setCurrentIndex(0);
29723
+ setCurrentMetadataIndex(0);
29724
+ currentMetadataIndexRef.current = 0;
29725
+ console.log(`[BottlenecksContent] Auto-loaded first video from cache: ${video.id} (1/${cachedMetadata.length})`);
29726
+ }
29727
+ } catch (error2) {
29728
+ console.error(`[BottlenecksContent] Error loading first video from cache:`, error2);
29729
+ clearLoadingState();
29473
29730
  }
29474
- } catch (error2) {
29475
- console.error(`[BottlenecksContent] Error loading first video from cache:`, error2);
29476
- setIsCategoryLoading(false);
29477
- clearLoadingState();
29478
29731
  }
29732
+ return;
29479
29733
  }
29480
- return;
29481
- }
29482
- try {
29483
- console.log(`[BottlenecksContent] Loading metadata for category: ${categoryId}`);
29734
+ console.log(`[BottlenecksContent] Loading metadata for category: ${categoryId}${forceRefresh ? " (force refresh)" : ""}`);
29484
29735
  const { createClient: createClient5 } = await import('@supabase/supabase-js');
29485
29736
  const supabase = createClient5(
29486
29737
  process.env.NEXT_PUBLIC_SUPABASE_URL || "",
@@ -29505,8 +29756,8 @@ var BottlenecksContent = ({
29505
29756
  action: "percentile-clips",
29506
29757
  percentileAction: percentileType,
29507
29758
  workspaceId,
29508
- startDate: `${date || getOperationalDate(timezone)}T00:00:00Z`,
29509
- endDate: `${date || getOperationalDate(timezone)}T23:59:59Z`,
29759
+ startDate: `${resolvedDate}T00:00:00Z`,
29760
+ endDate: `${resolvedDate}T23:59:59Z`,
29510
29761
  percentile: 10,
29511
29762
  shiftId: effectiveShift,
29512
29763
  limit: 100
@@ -29522,7 +29773,7 @@ var BottlenecksContent = ({
29522
29773
  body: JSON.stringify({
29523
29774
  action: "clip-metadata",
29524
29775
  workspaceId,
29525
- date: date || getOperationalDate(timezone),
29776
+ date: resolvedDate,
29526
29777
  shift: effectiveShift,
29527
29778
  category: categoryId,
29528
29779
  page: 1,
@@ -29567,19 +29818,22 @@ var BottlenecksContent = ({
29567
29818
  setCurrentIndex(0);
29568
29819
  setCurrentMetadataIndex(0);
29569
29820
  currentMetadataIndexRef.current = 0;
29570
- setIsCategoryLoading(false);
29571
29821
  console.log(`[BottlenecksContent] Auto-loaded first video: ${video.id} (1/${metadataClips.length})`);
29572
29822
  }
29573
29823
  } catch (error2) {
29574
29824
  console.error(`[BottlenecksContent] Error loading first video:`, error2);
29575
- setIsCategoryLoading(false);
29576
29825
  }
29577
29826
  }
29827
+ } else {
29828
+ setCategoryMetadata([]);
29829
+ categoryMetadataRef.current = [];
29578
29830
  }
29579
29831
  } catch (error2) {
29580
29832
  console.error(`[BottlenecksContent] Error loading category metadata:`, error2);
29833
+ } finally {
29834
+ setIsCategoryLoading(false);
29581
29835
  }
29582
- }, [workspaceId, date, effectiveShift, isPercentileCategory, metadataCache, s3ClipsService]);
29836
+ }, [workspaceId, date, effectiveShift, isPercentileCategory, metadataCache, s3ClipsService, timezone, clearLoadingState]);
29583
29837
  const loadAndPlayClipById = useCallback(async (clipId, categoryId, position) => {
29584
29838
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
29585
29839
  console.log(`[BottlenecksContent] Loading clip by ID: ${clipId}, category=${categoryId}, position=${position}`);
@@ -29603,21 +29857,31 @@ var BottlenecksContent = ({
29603
29857
  }
29604
29858
  try {
29605
29859
  await loadCategoryMetadata(categoryId, false);
29606
- const metadataArray = categoryMetadataRef.current;
29607
- if (metadataArray.length > 0) {
29608
- const clickedClipIndex = metadataArray.findIndex((clip) => clip.clipId === clipId);
29609
- if (clickedClipIndex !== -1) {
29610
- setCurrentMetadataIndex(clickedClipIndex);
29611
- currentMetadataIndexRef.current = clickedClipIndex;
29612
- const video = await s3ClipsService.getClipById(clipId);
29613
- if (video) {
29614
- setPendingVideo(video);
29615
- setCurrentClipId(clipId);
29616
- setAllVideos([video]);
29617
- setCurrentIndex(0);
29618
- console.log(`[BottlenecksContent] Loaded clip ${clipId} (${clickedClipIndex + 1}/${metadataArray.length})`);
29619
- }
29620
- }
29860
+ let metadataArray = categoryMetadataRef.current;
29861
+ const clipExistsInMetadata = metadataArray.some((clip) => clip.clipId === clipId);
29862
+ if (metadataArray.length === 0 || !clipExistsInMetadata) {
29863
+ console.warn(`[BottlenecksContent] Clip ${clipId} not found in metadata for ${categoryId} (cache hit: ${metadataArray.length > 0}) - forcing refresh`);
29864
+ await loadCategoryMetadata(categoryId, false, true);
29865
+ metadataArray = categoryMetadataRef.current;
29866
+ }
29867
+ if (metadataArray.length === 0) {
29868
+ throw new Error(`No metadata available for category ${categoryId}`);
29869
+ }
29870
+ const clickedClipIndex = metadataArray.findIndex((clip) => clip.clipId === clipId);
29871
+ if (clickedClipIndex === -1) {
29872
+ throw new Error(`Clip ${clipId} not found after metadata refresh`);
29873
+ }
29874
+ setCurrentMetadataIndex(clickedClipIndex);
29875
+ currentMetadataIndexRef.current = clickedClipIndex;
29876
+ const video = await s3ClipsService.getClipById(clipId);
29877
+ if (video) {
29878
+ setPendingVideo(video);
29879
+ setCurrentClipId(clipId);
29880
+ setAllVideos([video]);
29881
+ setCurrentIndex(0);
29882
+ console.log(`[BottlenecksContent] Loaded clip ${clipId} (${clickedClipIndex + 1}/${metadataArray.length})`);
29883
+ } else {
29884
+ throw new Error(`Failed to load video data for clip ${clipId}`);
29621
29885
  }
29622
29886
  } catch (error2) {
29623
29887
  console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
@@ -29631,7 +29895,7 @@ var BottlenecksContent = ({
29631
29895
  clearLoadingState();
29632
29896
  }
29633
29897
  }
29634
- }, [workspaceId, s3ClipsService, date, effectiveShift, updateActiveFilter, clearLoadingState]);
29898
+ }, [workspaceId, s3ClipsService, updateActiveFilter, clearLoadingState, loadCategoryMetadata]);
29635
29899
  useCallback(async (categoryId, clipIndex) => {
29636
29900
  console.warn("[BottlenecksContent] loadAndPlayClip is deprecated, use loadAndPlayClipById instead");
29637
29901
  if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
@@ -29658,7 +29922,7 @@ var BottlenecksContent = ({
29658
29922
  });
29659
29923
  setIsNavigating(false);
29660
29924
  }
29661
- }, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById]);
29925
+ }, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById, timezone]);
29662
29926
  const handleNext = useCallback(async () => {
29663
29927
  if (!isMountedRef.current) return;
29664
29928
  const currentFilter = activeFilterRef.current;
@@ -29675,8 +29939,18 @@ var BottlenecksContent = ({
29675
29939
  }
29676
29940
  try {
29677
29941
  const currentMetaIndex = currentMetadataIndexRef.current;
29678
- const metadataArray = categoryMetadataRef.current;
29942
+ let metadataArray = categoryMetadataRef.current;
29943
+ if (metadataArray.length === 0) {
29944
+ console.log(`[handleNext] Metadata empty for ${currentFilter}, loading before navigation`);
29945
+ await loadCategoryMetadata(currentFilter, false);
29946
+ metadataArray = categoryMetadataRef.current;
29947
+ }
29679
29948
  console.log(`[handleNext] Unified navigation: ${currentFilter}, metadata index: ${currentMetaIndex}/${metadataArray.length}`);
29949
+ if (metadataArray.length === 0) {
29950
+ console.warn("[handleNext] No metadata available after refresh - stopping navigation");
29951
+ clearLoadingState();
29952
+ return;
29953
+ }
29680
29954
  if (currentMetaIndex < metadataArray.length - 1) {
29681
29955
  const nextMetadataIndex = currentMetaIndex + 1;
29682
29956
  const nextClipMeta = metadataArray[nextMetadataIndex];
@@ -29711,7 +29985,7 @@ var BottlenecksContent = ({
29711
29985
  });
29712
29986
  clearLoadingState();
29713
29987
  }
29714
- }, [clearLoadingState, s3ClipsService]);
29988
+ }, [clearLoadingState, s3ClipsService, loadCategoryMetadata]);
29715
29989
  const handlePrevious = useCallback(async () => {
29716
29990
  if (!isMountedRef.current) return;
29717
29991
  const currentFilter = activeFilterRef.current;
@@ -29728,8 +30002,18 @@ var BottlenecksContent = ({
29728
30002
  }
29729
30003
  try {
29730
30004
  const currentMetaIndex = currentMetadataIndexRef.current;
29731
- const metadataArray = categoryMetadataRef.current;
30005
+ let metadataArray = categoryMetadataRef.current;
30006
+ if (metadataArray.length === 0) {
30007
+ console.log(`[handlePrevious] Metadata empty for ${currentFilter}, loading before navigation`);
30008
+ await loadCategoryMetadata(currentFilter, false);
30009
+ metadataArray = categoryMetadataRef.current;
30010
+ }
29732
30011
  console.log(`[handlePrevious] Unified navigation: ${currentFilter}, metadata index: ${currentMetaIndex}/${metadataArray.length}`);
30012
+ if (metadataArray.length === 0) {
30013
+ console.warn("[handlePrevious] No metadata available after refresh - stopping navigation");
30014
+ clearLoadingState();
30015
+ return;
30016
+ }
29733
30017
  if (currentMetaIndex > 0) {
29734
30018
  const prevMetadataIndex = currentMetaIndex - 1;
29735
30019
  const prevClipMeta = metadataArray[prevMetadataIndex];
@@ -29760,7 +30044,7 @@ var BottlenecksContent = ({
29760
30044
  });
29761
30045
  clearLoadingState();
29762
30046
  }
29763
- }, [clearLoadingState, s3ClipsService]);
30047
+ }, [clearLoadingState, s3ClipsService, loadCategoryMetadata]);
29764
30048
  const currentVideo = useMemo(() => {
29765
30049
  if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
29766
30050
  return null;
@@ -29773,7 +30057,10 @@ var BottlenecksContent = ({
29773
30057
  if (error?.isRetrying) {
29774
30058
  setError(null);
29775
30059
  }
29776
- }, [error]);
30060
+ if (videoRef.current?.playbackRate && playbackSpeed !== 1) {
30061
+ videoRef.current.playbackRate(playbackSpeed);
30062
+ }
30063
+ }, [error, playbackSpeed]);
29777
30064
  const handleVideoPlay = useCallback(async (player) => {
29778
30065
  setIsPlaying(true);
29779
30066
  setIsInitialLoading(false);
@@ -29943,7 +30230,13 @@ var BottlenecksContent = ({
29943
30230
  player.pause();
29944
30231
  }
29945
30232
  };
29946
- const toggleFullscreen = useCallback((e) => {
30233
+ const handlePlaybackSpeedChange = useCallback((speed) => {
30234
+ setPlaybackSpeed(speed);
30235
+ if (videoRef.current?.playbackRate) {
30236
+ videoRef.current.playbackRate(speed);
30237
+ }
30238
+ }, []);
30239
+ useCallback((e) => {
29947
30240
  e.stopPropagation();
29948
30241
  setIsFullscreen((prev) => !prev);
29949
30242
  }, []);
@@ -30117,12 +30410,7 @@ var BottlenecksContent = ({
30117
30410
  onLoadedData: handleLoadedData,
30118
30411
  onPlaying: handleVideoPlaying,
30119
30412
  onLoadingChange: handleVideoLoadingChange,
30120
- options: {
30121
- // Ensure full height is always visible - no cropping
30122
- fluid: false,
30123
- responsive: false,
30124
- fill: false
30125
- }
30413
+ options: videoPlayerOptions
30126
30414
  }
30127
30415
  )
30128
30416
  }
@@ -30183,58 +30471,7 @@ var BottlenecksContent = ({
30183
30471
  /* @__PURE__ */ jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
30184
30472
  /* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
30185
30473
  ] }) })
30186
- ),
30187
- /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
30188
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
30189
- /* @__PURE__ */ jsx(
30190
- "button",
30191
- {
30192
- onClick: (e) => {
30193
- e.stopPropagation();
30194
- togglePlayback();
30195
- },
30196
- className: "p-1.5 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
30197
- "aria-label": isPlaying ? "Pause" : "Play",
30198
- children: isPlaying ? /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1zm5 0a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8.118l-.001 3.764a1 1 0 001.555.832l3.196-1.882a1 1 0 000-1.664l-3.196-1.882z", clipRule: "evenodd" }) })
30199
- }
30200
- ),
30201
- /* @__PURE__ */ jsxs("span", { className: "text-xs font-mono px-2", children: [
30202
- formatTime2(currentTime),
30203
- " / ",
30204
- formatTime2(duration)
30205
- ] })
30206
- ] }),
30207
- /* @__PURE__ */ jsx(
30208
- "input",
30209
- {
30210
- type: "range",
30211
- min: "0",
30212
- max: duration || 0,
30213
- value: currentTime,
30214
- onChange: (e) => {
30215
- if (videoRef.current) {
30216
- videoRef.current.currentTime(Number(e.target.value));
30217
- }
30218
- },
30219
- className: "flex-grow mx-3 h-2.5 bg-white/30 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-white/50 touch-manipulation",
30220
- style: {
30221
- WebkitAppearance: "none",
30222
- appearance: "none"
30223
- },
30224
- "aria-label": "Seek slider"
30225
- }
30226
- ),
30227
- /* @__PURE__ */ jsx(
30228
- "button",
30229
- {
30230
- onClick: toggleFullscreen,
30231
- className: "p-1.5 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
30232
- "aria-label": "Fullscreen",
30233
- title: "Expand to fullscreen",
30234
- children: /* @__PURE__ */ jsx(Maximize2, { className: "h-5 w-5" })
30235
- }
30236
- )
30237
- ] }) })
30474
+ )
30238
30475
  ] }) }) }) : (
30239
30476
  /* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
30240
30477
  hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsx("div", { className: `flex items-center justify-center ${triageMode ? "h-full" : "h-[calc(100%-4rem)]"}`, children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
@@ -30452,11 +30689,7 @@ var BottlenecksContent = ({
30452
30689
  onLoadedData: handleLoadedData,
30453
30690
  onPlaying: handleVideoPlaying,
30454
30691
  onLoadingChange: handleVideoLoadingChange,
30455
- options: {
30456
- fluid: false,
30457
- responsive: false,
30458
- fill: false
30459
- }
30692
+ options: videoPlayerOptions
30460
30693
  }
30461
30694
  )
30462
30695
  }
@@ -30464,45 +30697,32 @@ var BottlenecksContent = ({
30464
30697
  (isTransitioning || isVideoBuffering && isInitialLoading) && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
30465
30698
  !isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
30466
30699
  /* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
30467
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
30468
- /* @__PURE__ */ jsx(
30469
- "button",
30700
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
30701
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Speed:" }),
30702
+ /* @__PURE__ */ jsxs(
30703
+ "select",
30470
30704
  {
30471
- onClick: (e) => {
30472
- e.stopPropagation();
30473
- togglePlayback();
30474
- },
30475
- className: "p-2 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
30476
- "aria-label": isPlaying ? "Pause" : "Play",
30477
- children: isPlaying ? /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-6 w-6", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1zm5 0a1 1 0 00-1 1v2a1 1 0 102 0V9a1 1 0 00-1-1z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-6 w-6", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8.118l-.001 3.764a1 1 0 001.555.832l3.196-1.882a1 1 0 000-1.664l-3.196-1.882z", clipRule: "evenodd" }) })
30705
+ value: playbackSpeed,
30706
+ onChange: (e) => handlePlaybackSpeedChange(Number(e.target.value)),
30707
+ className: "px-2 py-1 bg-white/20 hover:bg-white/30 rounded text-sm font-medium cursor-pointer focus:outline-none focus:ring-2 focus:ring-white/50 transition-colors",
30708
+ "aria-label": "Playback speed",
30709
+ children: [
30710
+ /* @__PURE__ */ jsx("option", { value: "0.25", children: "0.25x" }),
30711
+ /* @__PURE__ */ jsx("option", { value: "0.5", children: "0.5x" }),
30712
+ /* @__PURE__ */ jsx("option", { value: "0.75", children: "0.75x" }),
30713
+ /* @__PURE__ */ jsx("option", { value: "1", children: "1x" }),
30714
+ /* @__PURE__ */ jsx("option", { value: "1.25", children: "1.25x" }),
30715
+ /* @__PURE__ */ jsx("option", { value: "1.5", children: "1.5x" }),
30716
+ /* @__PURE__ */ jsx("option", { value: "1.75", children: "1.75x" }),
30717
+ /* @__PURE__ */ jsx("option", { value: "2", children: "2x" }),
30718
+ /* @__PURE__ */ jsx("option", { value: "3", children: "3x" }),
30719
+ /* @__PURE__ */ jsx("option", { value: "4", children: "4x" }),
30720
+ /* @__PURE__ */ jsx("option", { value: "5", children: "5x" }),
30721
+ /* @__PURE__ */ jsx("option", { value: "10", children: "10x" })
30722
+ ]
30478
30723
  }
30479
- ),
30480
- /* @__PURE__ */ jsxs("span", { className: "text-sm font-mono px-2", children: [
30481
- formatTime2(currentTime),
30482
- " / ",
30483
- formatTime2(duration)
30484
- ] })
30724
+ )
30485
30725
  ] }),
30486
- /* @__PURE__ */ jsx(
30487
- "input",
30488
- {
30489
- type: "range",
30490
- min: "0",
30491
- max: duration || 0,
30492
- value: currentTime,
30493
- onChange: (e) => {
30494
- if (videoRef.current) {
30495
- videoRef.current.currentTime(Number(e.target.value));
30496
- }
30497
- },
30498
- className: "flex-grow mx-4 h-2.5 bg-white/30 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-white/50 touch-manipulation",
30499
- style: {
30500
- WebkitAppearance: "none",
30501
- appearance: "none"
30502
- },
30503
- "aria-label": "Seek slider"
30504
- }
30505
- ),
30506
30726
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
30507
30727
  /* @__PURE__ */ jsx(
30508
30728
  "button",
@@ -37603,26 +37823,26 @@ var SingleVideoStream = ({
37603
37823
  const hlsStreamUrl = streamUrl || getCameraStreamUrl(workspaceName, baseUrl);
37604
37824
  console.log(`Using camera URL for ${workspaceName}: ${hlsStreamUrl}`);
37605
37825
  const mergedHlsConfig = { ...DEFAULT_HLS_CONFIG, ...hlsConfig };
37606
- if (Hls2.isSupported()) {
37607
- const hls = new Hls2(mergedHlsConfig);
37826
+ if (Hls3.isSupported()) {
37827
+ const hls = new Hls3(mergedHlsConfig);
37608
37828
  hlsRef.current = hls;
37609
- hls.on(Hls2.Events.MEDIA_ATTACHED, () => {
37829
+ hls.on(Hls3.Events.MEDIA_ATTACHED, () => {
37610
37830
  console.log("HLS media attached");
37611
37831
  hls.loadSource(hlsStreamUrl);
37612
37832
  });
37613
- hls.on(Hls2.Events.MANIFEST_PARSED, () => {
37833
+ hls.on(Hls3.Events.MANIFEST_PARSED, () => {
37614
37834
  console.log("HLS manifest parsed");
37615
37835
  attemptPlay(video);
37616
37836
  });
37617
- hls.on(Hls2.Events.ERROR, (_, data) => {
37837
+ hls.on(Hls3.Events.ERROR, (_, data) => {
37618
37838
  if (data.fatal) {
37619
37839
  console.error("Fatal HLS error:", data.type, data.details);
37620
37840
  switch (data.type) {
37621
- case Hls2.ErrorTypes.NETWORK_ERROR:
37841
+ case Hls3.ErrorTypes.NETWORK_ERROR:
37622
37842
  console.error("Fatal network error encountered");
37623
37843
  setError("Network error: Please check your connection");
37624
37844
  break;
37625
- case Hls2.ErrorTypes.MEDIA_ERROR:
37845
+ case Hls3.ErrorTypes.MEDIA_ERROR:
37626
37846
  console.error("Fatal media error encountered, trying to recover");
37627
37847
  hls.recoverMediaError();
37628
37848
  break;
@@ -42383,9 +42603,9 @@ var MetricCards = memo(({ lineInfo }) => {
42383
42603
  animate: "animate",
42384
42604
  className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 mb-2 md:h-[35vh] h-auto",
42385
42605
  children: [
42386
- /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[200px] sm:h-[280px] md:h-auto lg:col-span-1 sm:col-span-2 lg:col-span-1", children: [
42387
- /* @__PURE__ */ jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 mb-1 sm:mb-2 text-center", children: "Line Output" }),
42388
- /* @__PURE__ */ jsx("div", { className: "h-[calc(100%-1.75rem)] sm:h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsx(
42606
+ /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
42607
+ /* @__PURE__ */ jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 mb-2 text-center", children: "Line Output" }),
42608
+ /* @__PURE__ */ jsx("div", { className: "h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsx(
42389
42609
  OutputProgressChart,
42390
42610
  {
42391
42611
  currentOutput: lineInfo?.metrics.current_output || 0,
@@ -42393,9 +42613,9 @@ var MetricCards = memo(({ lineInfo }) => {
42393
42613
  }
42394
42614
  ) }) })
42395
42615
  ] }),
42396
- /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[140px] sm:h-[160px] md:h-auto", children: [
42616
+ /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
42397
42617
  /* @__PURE__ */ jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 text-center mb-2", children: "Underperforming Workspaces" }),
42398
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center h-[calc(100%-2rem)] sm:h-[calc(100%-2.5rem)]", children: [
42618
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: [
42399
42619
  /* @__PURE__ */ jsx("span", { className: "text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-red-600", children: lineInfo?.metrics.underperforming_workspaces }),
42400
42620
  /* @__PURE__ */ jsxs("span", { className: "text-xl sm:text-2xl md:text-2xl lg:text-3xl text-gray-500 ml-1 sm:ml-2", children: [
42401
42621
  "/ ",
@@ -42403,9 +42623,9 @@ var MetricCards = memo(({ lineInfo }) => {
42403
42623
  ] })
42404
42624
  ] })
42405
42625
  ] }),
42406
- /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[140px] sm:h-[160px] md:h-auto", children: [
42626
+ /* @__PURE__ */ jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[240px] sm:h-[260px] md:h-auto", children: [
42407
42627
  /* @__PURE__ */ jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 text-center mb-2", children: "Average Efficiency" }),
42408
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-2rem)] sm:h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxs("span", { className: `text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold ${(lineInfo?.metrics.avg_efficiency || 0) >= 90 ? "text-green-600" : (lineInfo?.metrics.avg_efficiency || 0) >= 70 ? "text-yellow-600" : "text-red-600"}`, children: [
42628
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxs("span", { className: `text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold ${(lineInfo?.metrics.avg_efficiency || 0) >= 90 ? "text-green-600" : (lineInfo?.metrics.avg_efficiency || 0) >= 70 ? "text-yellow-600" : "text-red-600"}`, children: [
42409
42629
  lineInfo?.metrics.avg_efficiency.toFixed(1),
42410
42630
  "%"
42411
42631
  ] }) })
@@ -51923,4 +52143,4 @@ function shuffleArray(array) {
51923
52143
  return shuffled;
51924
52144
  }
51925
52145
 
51926
- export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, 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_MAP_VIEW_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, DiagnosisVideoModal, 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, InvitationService, 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, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, 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, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, 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, createInvitationService, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, 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, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };
52146
+ export { ACTION_NAMES, AIAgentView_default as AIAgentView, AcceptInvite, AcceptInviteView_default as AcceptInviteView, AdvancedFilterDialog, AdvancedFilterPanel, AudioService, AuthCallback, AuthCallbackView_default as AuthCallbackView, AuthProvider, AuthService, AuthenticatedBottleneckClipsView, AuthenticatedFactoryView, AuthenticatedHelpView, AuthenticatedHomeView, AuthenticatedShiftsView, AuthenticatedTargetsView, AuthenticatedTicketsView, AuthenticatedWorkspaceHealthView, AxelNotificationPopup, AxelOrb, BackButton, BackButtonMinimal, BarChart, BaseHistoryCalendar, BottleneckClipsModal, BottleneckClipsView_default as BottleneckClipsView, BottlenecksContent, BreakNotificationPopup, CachePrefetchStatus, Card2 as Card, CardContent2 as CardContent, CardDescription2 as CardDescription, CardFooter2 as CardFooter, CardHeader2 as CardHeader, CardTitle2 as CardTitle, ClipFilterProvider, CompactWorkspaceHealthCard, CongratulationsOverlay, CroppedHlsVideoPlayer, CroppedVideoPlayer, CycleTimeChart, CycleTimeOverTimeChart, DEFAULT_ANALYTICS_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_CONFIG, DEFAULT_DATABASE_CONFIG, DEFAULT_DATE_TIME_CONFIG, DEFAULT_ENDPOINTS_CONFIG, DEFAULT_ENTITY_CONFIG, DEFAULT_MAP_VIEW_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, DiagnosisVideoModal, EmptyStateMessage, EncouragementOverlay, FactoryView_default as FactoryView, FileManagerFilters, FilterDialogTrigger, FirstTimeLoginDebug, FirstTimeLoginHandler, GaugeChart, GridComponentsPlaceholder, HamburgerButton, Header, HealthStatusGrid, HealthStatusIndicator, HelpView_default as HelpView, HlsVideoPlayer, HomeView_default as HomeView, HourlyOutputChart2 as HourlyOutputChart, ISTTimer_default as ISTTimer, InlineEditableText, InteractiveOnboardingTour, InvitationService, 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, Logo, MainLayout, MapGridView, MetricCard_default as MetricCard, MinimalOnboardingPopup, NewClipsNotification, NoWorkspaceData, OnboardingDemo, OnboardingTour, OptifyeAgentClient, OptifyeLogoLoader_default as OptifyeLogoLoader, OutputProgressChart, PageHeader, PieChart4 as PieChart, PlayPauseIndicator, 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, SignupWithInvitation, SilentErrorBoundary, SimpleOnboardingPopup, SingleVideoStream_default as SingleVideoStream, Skeleton, SubscriptionManager, SubscriptionManagerProvider, SupabaseProvider, SupervisorDropdown_default as SupervisorDropdown, SupervisorManagementView_default as SupervisorManagementView, SupervisorService, TargetWorkspaceGrid, TargetsView_default as TargetsView, TeamManagementView_default as TeamManagementView, ThreadSidebar, TicketHistory_default as TicketHistory, TicketHistoryService, TicketsView_default as TicketsView, TimeDisplay_default as TimeDisplay, TimePickerDropdown, Timer_default as Timer, TimezoneProvider, TimezoneService, UserManagementService, 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, createInvitationService, createLinesService, createStreamProxyHandler, createSupabaseClient, createSupervisorService, createThrottledReload, createUserManagementService, createUserService, dashboardService, deleteThread, forceRefreshWorkspaceDisplayNames, formatDateInZone, formatDateTimeInZone, formatISTDate, formatIdleTime, formatRelativeTime, formatTimeInZone, fromUrlFriendlyName, getAllLineDisplayNames, getAllThreadMessages, getAllWorkspaceDisplayNamesAsync, getAnonClient, getBrowserName, getCameraNumber, getCompanyMetricsTableName, getConfigurableShortWorkspaceDisplayName, getConfigurableWorkspaceDisplayName, getConfiguredLineIds, getCoreSessionRecordingProperties, getCoreSessionReplayUrl, getCurrentShift, getCurrentTimeInZone, getDashboardHeaderTimeInZone, getDaysDifferenceInZone, getDefaultCameraStreamUrl, getDefaultLineId, getDefaultTabForWorkspace, getLineDisplayName, getManufacturingInsights, getMetricsTablePrefix, getNextUpdateInterval, getOperationalDate, getS3SignedUrl, getS3VideoSrc, getShortWorkspaceDisplayName, getShortWorkspaceDisplayNameAsync, getStoredWorkspaceMappings, getSubscriptionManager, getThreadMessages, getUserThreads, getUserThreadsPaginated, getWorkspaceDisplayName, getWorkspaceDisplayNameAsync, getWorkspaceDisplayNamesMap, getWorkspaceFromUrl, getWorkspaceNavigationParams, identifyCoreUser, initializeCoreMixpanel, isLegacyConfiguration, isPrefetchError, isSafari, 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, useActiveLineId, useAllWorkspaceMetrics, useAnalyticsConfig, useAppTimezone, useAudioService, useAuth, useAuthConfig, useAxelNotifications, useCanSaveTargets, useClipFilter, useClipTypes, useClipTypesWithCounts, useComponentOverride, useCustomConfig, useDashboardConfig, useDashboardMetrics, useDatabaseConfig, useDateFormatter, useDateTimeConfig, useDynamicShiftConfig, useEndpointsConfig, useEntityConfig, useFactoryOverviewMetrics, useFeatureFlags, useFormatNumber, useHasLineAccess, useHistoricWorkspaceMetrics, useHlsStream, useHlsStreamWithCropping, useHookOverride, useHourEndTimer, useHourlyTargetAchievements, useHourlyTargetMisses, useLeaderboardMetrics, useLineDetailedMetrics, useLineKPIs, useLineMetrics, useLineShiftConfig, useLineSupervisor, useLineWorkspaceMetrics, useMessages, useMetrics, useNavigation, useOverrides, usePageOverride, usePrefetchClipCounts, useRealtimeLineMetrics, useRegistry, useSKUs, useSessionKeepAlive, useShiftConfig, useShifts, useSubscriptionManager, useSubscriptionManagerSafe, useSupabase, useSupabaseClient, useTargets, useTeamManagementPermissions, useTheme, useThemeConfig, useThreads, useTicketHistory, useTimezoneContext, useUserLineAccess, useVideoConfig, useWorkspaceConfig, useWorkspaceDetailedMetrics, useWorkspaceDisplayName, useWorkspaceDisplayNames, useWorkspaceDisplayNamesMap, useWorkspaceHealthById, useWorkspaceHealthStatus, useWorkspaceMetrics, useWorkspaceNavigation, useWorkspaceOperators, userService, videoPrefetchManager, videoPreloader, whatsappService, withAccessControl, withAuth, withRegistry, withTimezone, workspaceHealthService, workspaceService };