@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.css +4 -36
- package/dist/index.d.mts +58 -37
- package/dist/index.d.ts +58 -37
- package/dist/index.js +1596 -1375
- package/dist/index.mjs +1595 -1375
- package/package.json +1 -3
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ var dateFns = require('date-fns');
|
|
|
8
8
|
var mixpanel = require('mixpanel-browser');
|
|
9
9
|
var events = require('events');
|
|
10
10
|
var supabaseJs = require('@supabase/supabase-js');
|
|
11
|
-
var
|
|
11
|
+
var Hls3 = require('hls.js');
|
|
12
12
|
var useSWR = require('swr');
|
|
13
13
|
var motionUtils = require('motion-utils');
|
|
14
14
|
var motionDom = require('motion-dom');
|
|
@@ -17,8 +17,6 @@ var sonner = require('sonner');
|
|
|
17
17
|
var recharts = require('recharts');
|
|
18
18
|
var reactSlot = require('@radix-ui/react-slot');
|
|
19
19
|
var SelectPrimitive = require('@radix-ui/react-select');
|
|
20
|
-
var videojs = require('video.js');
|
|
21
|
-
require('video.js/dist/video-js.css');
|
|
22
20
|
var reactDayPicker = require('react-day-picker');
|
|
23
21
|
var outline = require('@heroicons/react/24/outline');
|
|
24
22
|
var solid = require('@heroicons/react/24/solid');
|
|
@@ -51,10 +49,9 @@ function _interopNamespace(e) {
|
|
|
51
49
|
|
|
52
50
|
var React23__namespace = /*#__PURE__*/_interopNamespace(React23);
|
|
53
51
|
var mixpanel__default = /*#__PURE__*/_interopDefault(mixpanel);
|
|
54
|
-
var
|
|
52
|
+
var Hls3__default = /*#__PURE__*/_interopDefault(Hls3);
|
|
55
53
|
var useSWR__default = /*#__PURE__*/_interopDefault(useSWR);
|
|
56
54
|
var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
|
|
57
|
-
var videojs__default = /*#__PURE__*/_interopDefault(videojs);
|
|
58
55
|
var html2canvas__default = /*#__PURE__*/_interopDefault(html2canvas);
|
|
59
56
|
var jsPDF__default = /*#__PURE__*/_interopDefault(jsPDF);
|
|
60
57
|
|
|
@@ -9019,12 +9016,12 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
|
|
|
9019
9016
|
const video = videoRef.current;
|
|
9020
9017
|
if (!video) return;
|
|
9021
9018
|
isNativeHlsRef.current = video.canPlayType("application/vnd.apple.mpegurl") === "probably";
|
|
9022
|
-
if (
|
|
9023
|
-
const hls = new
|
|
9019
|
+
if (Hls3__default.default.isSupported() && !isNativeHlsRef.current) {
|
|
9020
|
+
const hls = new Hls3__default.default(HLS_CONFIG);
|
|
9024
9021
|
hlsRef.current = hls;
|
|
9025
9022
|
hls.attachMedia(video);
|
|
9026
9023
|
hls.loadSource(src);
|
|
9027
|
-
hls.on(
|
|
9024
|
+
hls.on(Hls3__default.default.Events.ERROR, (_, data) => {
|
|
9028
9025
|
if (!data.fatal) return;
|
|
9029
9026
|
console.error("[HLS] Fatal error:", data.type, data.details);
|
|
9030
9027
|
if (data.response?.code === 404) {
|
|
@@ -9032,8 +9029,8 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
|
|
|
9032
9029
|
return;
|
|
9033
9030
|
}
|
|
9034
9031
|
switch (data.type) {
|
|
9035
|
-
case
|
|
9036
|
-
case
|
|
9032
|
+
case Hls3__default.default.ErrorTypes.NETWORK_ERROR:
|
|
9033
|
+
case Hls3__default.default.ErrorTypes.MEDIA_ERROR:
|
|
9037
9034
|
softRestart(`${data.type}: ${data.details}`);
|
|
9038
9035
|
break;
|
|
9039
9036
|
default:
|
|
@@ -9041,7 +9038,7 @@ function useHlsStream(videoRef, { src, shouldPlay, onFatalError }) {
|
|
|
9041
9038
|
break;
|
|
9042
9039
|
}
|
|
9043
9040
|
});
|
|
9044
|
-
hls.on(
|
|
9041
|
+
hls.on(Hls3__default.default.Events.MANIFEST_PARSED, () => {
|
|
9045
9042
|
if (failedUrls.has(src)) {
|
|
9046
9043
|
console.log(`[HLS] Stream loaded successfully, resetting failure count for: ${src}`);
|
|
9047
9044
|
failedUrls.delete(src);
|
|
@@ -14084,9 +14081,9 @@ var S3VideoPreloader = class {
|
|
|
14084
14081
|
this.processQueue();
|
|
14085
14082
|
};
|
|
14086
14083
|
if (url.endsWith(".m3u8")) {
|
|
14087
|
-
import('hls.js').then(({ default:
|
|
14088
|
-
if (
|
|
14089
|
-
const hls = new
|
|
14084
|
+
import('hls.js').then(({ default: Hls4 }) => {
|
|
14085
|
+
if (Hls4.isSupported()) {
|
|
14086
|
+
const hls = new Hls4({
|
|
14090
14087
|
maxBufferLength: 10,
|
|
14091
14088
|
startFragPrefetch: true,
|
|
14092
14089
|
lowLatencyMode: false,
|
|
@@ -14098,7 +14095,7 @@ var S3VideoPreloader = class {
|
|
|
14098
14095
|
hls.destroy();
|
|
14099
14096
|
cleanup();
|
|
14100
14097
|
}, 4e3);
|
|
14101
|
-
hls.on(
|
|
14098
|
+
hls.on(Hls4.Events.BUFFER_APPENDED, () => {
|
|
14102
14099
|
window.clearTimeout(timeout);
|
|
14103
14100
|
hls.destroy();
|
|
14104
14101
|
cleanup();
|
|
@@ -23102,7 +23099,7 @@ var OutputProgressChartComponent = ({
|
|
|
23102
23099
|
];
|
|
23103
23100
|
const COLORS = ["#00AB45", "#f3f4f6"];
|
|
23104
23101
|
const percentage = (currentOutput / targetOutput * 100).toFixed(1);
|
|
23105
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full
|
|
23102
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full aspect-square max-h-full", style: { maxWidth: "min(100%, 280px)", containerType: "inline-size" }, children: [
|
|
23106
23103
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.PieChart, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23107
23104
|
recharts.Pie,
|
|
23108
23105
|
{
|
|
@@ -23126,16 +23123,33 @@ var OutputProgressChartComponent = ({
|
|
|
23126
23123
|
))
|
|
23127
23124
|
}
|
|
23128
23125
|
) }) }),
|
|
23129
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
23130
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
23131
|
-
|
|
23132
|
-
|
|
23133
|
-
|
|
23134
|
-
|
|
23135
|
-
|
|
23136
|
-
|
|
23137
|
-
|
|
23138
|
-
|
|
23126
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", style: { width: "100%", padding: "0 10%" }, children: [
|
|
23127
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
23128
|
+
"div",
|
|
23129
|
+
{
|
|
23130
|
+
className: "font-bold text-gray-800",
|
|
23131
|
+
style: { fontSize: "clamp(1.25rem, 8cqw, 2.5rem)" },
|
|
23132
|
+
children: [
|
|
23133
|
+
percentage,
|
|
23134
|
+
"%"
|
|
23135
|
+
]
|
|
23136
|
+
}
|
|
23137
|
+
),
|
|
23138
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
23139
|
+
"div",
|
|
23140
|
+
{
|
|
23141
|
+
className: "text-gray-500",
|
|
23142
|
+
style: {
|
|
23143
|
+
fontSize: "clamp(0.7rem, 3.5cqw, 1rem)",
|
|
23144
|
+
marginTop: "clamp(0.125rem, 1cqw, 0.5rem)"
|
|
23145
|
+
},
|
|
23146
|
+
children: [
|
|
23147
|
+
currentOutput,
|
|
23148
|
+
" / ",
|
|
23149
|
+
Math.round(targetOutput)
|
|
23150
|
+
]
|
|
23151
|
+
}
|
|
23152
|
+
)
|
|
23139
23153
|
] }) })
|
|
23140
23154
|
] }) });
|
|
23141
23155
|
};
|
|
@@ -23153,7 +23167,7 @@ var LargeOutputProgressChart = ({
|
|
|
23153
23167
|
];
|
|
23154
23168
|
const COLORS = ["#00AB45", "#f3f4f6"];
|
|
23155
23169
|
const percentage = (currentOutput / targetOutput * 100).toFixed(1);
|
|
23156
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full", style: { minHeight: "100px", minWidth: "100px" }, children: [
|
|
23170
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-full h-full flex items-center justify-center ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full", style: { minHeight: "100px", minWidth: "100px", containerType: "inline-size" }, children: [
|
|
23157
23171
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.PieChart, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23158
23172
|
recharts.Pie,
|
|
23159
23173
|
{
|
|
@@ -23177,16 +23191,40 @@ var LargeOutputProgressChart = ({
|
|
|
23177
23191
|
))
|
|
23178
23192
|
}
|
|
23179
23193
|
) }) }),
|
|
23180
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
23181
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
23182
|
-
|
|
23183
|
-
|
|
23184
|
-
|
|
23185
|
-
|
|
23186
|
-
|
|
23187
|
-
|
|
23188
|
-
|
|
23189
|
-
|
|
23194
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", style: { width: "100%", padding: "0 10%" }, children: [
|
|
23195
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
23196
|
+
"div",
|
|
23197
|
+
{
|
|
23198
|
+
className: "font-bold text-gray-900",
|
|
23199
|
+
style: { fontSize: "clamp(1.5rem, 10cqw, 3rem)" },
|
|
23200
|
+
children: currentOutput
|
|
23201
|
+
}
|
|
23202
|
+
),
|
|
23203
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
23204
|
+
"div",
|
|
23205
|
+
{
|
|
23206
|
+
className: "text-gray-500",
|
|
23207
|
+
style: { fontSize: "clamp(0.75rem, 3.5cqw, 1rem)" },
|
|
23208
|
+
children: [
|
|
23209
|
+
"of ",
|
|
23210
|
+
targetOutput
|
|
23211
|
+
]
|
|
23212
|
+
}
|
|
23213
|
+
),
|
|
23214
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
23215
|
+
"div",
|
|
23216
|
+
{
|
|
23217
|
+
className: "font-medium text-gray-600",
|
|
23218
|
+
style: {
|
|
23219
|
+
fontSize: "clamp(0.875rem, 4.5cqw, 1.25rem)",
|
|
23220
|
+
marginTop: "clamp(0.125rem, 1cqw, 0.5rem)"
|
|
23221
|
+
},
|
|
23222
|
+
children: [
|
|
23223
|
+
percentage,
|
|
23224
|
+
"%"
|
|
23225
|
+
]
|
|
23226
|
+
}
|
|
23227
|
+
)
|
|
23190
23228
|
] }) })
|
|
23191
23229
|
] }) });
|
|
23192
23230
|
};
|
|
@@ -25280,7 +25318,7 @@ var GaugeChart = ({
|
|
|
25280
25318
|
};
|
|
25281
25319
|
const gaugeColor = getColor();
|
|
25282
25320
|
const targetAngle = target !== void 0 ? 180 - (target - min) / (max - min) * 180 : null;
|
|
25283
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `relative w-full h-full flex flex-col items-center justify-center ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full max-w-[280px] aspect-square", style: { minHeight: "100px", minWidth: "100px" }, children: [
|
|
25321
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `relative w-full h-full flex flex-col items-center justify-center ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full max-w-[280px] aspect-square", style: { minHeight: "100px", minWidth: "100px", containerType: "inline-size" }, children: [
|
|
25284
25322
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.PieChart, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
25285
25323
|
recharts.Pie,
|
|
25286
25324
|
{
|
|
@@ -25313,17 +25351,44 @@ var GaugeChart = ({
|
|
|
25313
25351
|
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-1 -left-1.5 w-3 h-3 bg-gray-800 rounded-full" })
|
|
25314
25352
|
}
|
|
25315
25353
|
),
|
|
25316
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
25317
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
25318
|
-
|
|
25319
|
-
|
|
25320
|
-
|
|
25321
|
-
|
|
25322
|
-
|
|
25323
|
-
|
|
25324
|
-
|
|
25325
|
-
|
|
25326
|
-
|
|
25354
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", style: { width: "100%", padding: "0 10%" }, children: [
|
|
25355
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
25356
|
+
"div",
|
|
25357
|
+
{
|
|
25358
|
+
className: "font-bold text-gray-800",
|
|
25359
|
+
style: { fontSize: "clamp(1.25rem, 8cqw, 2rem)" },
|
|
25360
|
+
children: [
|
|
25361
|
+
value.toFixed(unit === "%" ? 1 : 0),
|
|
25362
|
+
unit
|
|
25363
|
+
]
|
|
25364
|
+
}
|
|
25365
|
+
),
|
|
25366
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
25367
|
+
"div",
|
|
25368
|
+
{
|
|
25369
|
+
className: "text-gray-600 font-medium",
|
|
25370
|
+
style: {
|
|
25371
|
+
fontSize: "clamp(0.75rem, 3.5cqw, 1rem)",
|
|
25372
|
+
marginTop: "clamp(0.125rem, 1cqw, 0.25rem)"
|
|
25373
|
+
},
|
|
25374
|
+
children: label
|
|
25375
|
+
}
|
|
25376
|
+
),
|
|
25377
|
+
target !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
25378
|
+
"div",
|
|
25379
|
+
{
|
|
25380
|
+
className: "text-gray-500",
|
|
25381
|
+
style: {
|
|
25382
|
+
fontSize: "clamp(0.7rem, 3cqw, 0.875rem)",
|
|
25383
|
+
marginTop: "clamp(0.125rem, 1cqw, 0.25rem)"
|
|
25384
|
+
},
|
|
25385
|
+
children: [
|
|
25386
|
+
"Target: ",
|
|
25387
|
+
target,
|
|
25388
|
+
unit
|
|
25389
|
+
]
|
|
25390
|
+
}
|
|
25391
|
+
)
|
|
25327
25392
|
] }) }),
|
|
25328
25393
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-[15%] left-[15%] text-xs text-gray-500", children: [
|
|
25329
25394
|
min,
|
|
@@ -26054,12 +26119,6 @@ var AxelNotificationPopup = ({
|
|
|
26054
26119
|
};
|
|
26055
26120
|
|
|
26056
26121
|
// src/views/components/workspace/BottlenecksContent.utils.ts
|
|
26057
|
-
var formatTime2 = (seconds) => {
|
|
26058
|
-
if (isNaN(seconds)) return "0:00";
|
|
26059
|
-
const minutes = Math.floor(seconds / 60);
|
|
26060
|
-
const remainingSeconds = Math.floor(seconds % 60);
|
|
26061
|
-
return `${minutes}:${remainingSeconds < 10 ? "0" : ""}${remainingSeconds}`;
|
|
26062
|
-
};
|
|
26063
26122
|
var getSeverityColor = (severity) => {
|
|
26064
26123
|
switch (severity) {
|
|
26065
26124
|
case "low":
|
|
@@ -26072,957 +26131,575 @@ var getSeverityColor = (severity) => {
|
|
|
26072
26131
|
return "bg-gray-500";
|
|
26073
26132
|
}
|
|
26074
26133
|
};
|
|
26075
|
-
|
|
26076
|
-
|
|
26077
|
-
|
|
26078
|
-
|
|
26079
|
-
var SelectGroup = SelectPrimitive__namespace.Group;
|
|
26080
|
-
var SelectValue = SelectPrimitive__namespace.Value;
|
|
26081
|
-
var SelectTrigger = React23__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26082
|
-
SelectPrimitive__namespace.Trigger,
|
|
26083
|
-
{
|
|
26084
|
-
ref,
|
|
26085
|
-
className: cn(
|
|
26086
|
-
"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",
|
|
26087
|
-
className
|
|
26088
|
-
),
|
|
26089
|
-
...props,
|
|
26090
|
-
children: [
|
|
26091
|
-
children,
|
|
26092
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
26093
|
-
]
|
|
26094
|
-
}
|
|
26095
|
-
));
|
|
26096
|
-
SelectTrigger.displayName = SelectPrimitive__namespace.Trigger.displayName;
|
|
26097
|
-
var SelectScrollUpButton = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
26098
|
-
SelectPrimitive__namespace.ScrollUpButton,
|
|
26099
|
-
{
|
|
26100
|
-
ref,
|
|
26101
|
-
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
26102
|
-
...props,
|
|
26103
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4" })
|
|
26104
|
-
}
|
|
26105
|
-
));
|
|
26106
|
-
SelectScrollUpButton.displayName = SelectPrimitive__namespace.ScrollUpButton.displayName;
|
|
26107
|
-
var SelectScrollDownButton = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
26108
|
-
SelectPrimitive__namespace.ScrollDownButton,
|
|
26109
|
-
{
|
|
26110
|
-
ref,
|
|
26111
|
-
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
26112
|
-
...props,
|
|
26113
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4" })
|
|
26114
|
-
}
|
|
26115
|
-
));
|
|
26116
|
-
SelectScrollDownButton.displayName = SelectPrimitive__namespace.ScrollDownButton.displayName;
|
|
26117
|
-
var SelectContent = React23__namespace.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26118
|
-
SelectPrimitive__namespace.Content,
|
|
26119
|
-
{
|
|
26120
|
-
ref,
|
|
26121
|
-
className: cn(
|
|
26122
|
-
"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]",
|
|
26123
|
-
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",
|
|
26124
|
-
className
|
|
26125
|
-
),
|
|
26126
|
-
position,
|
|
26127
|
-
...props,
|
|
26128
|
-
children: [
|
|
26129
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
|
|
26130
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26131
|
-
SelectPrimitive__namespace.Viewport,
|
|
26132
|
-
{
|
|
26133
|
-
className: cn(
|
|
26134
|
-
"p-1",
|
|
26135
|
-
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
26136
|
-
),
|
|
26137
|
-
children
|
|
26138
|
-
}
|
|
26139
|
-
),
|
|
26140
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
|
|
26141
|
-
]
|
|
26142
|
-
}
|
|
26143
|
-
) }));
|
|
26144
|
-
SelectContent.displayName = SelectPrimitive__namespace.Content.displayName;
|
|
26145
|
-
var SelectLabel = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
26146
|
-
SelectPrimitive__namespace.Label,
|
|
26147
|
-
{
|
|
26148
|
-
ref,
|
|
26149
|
-
className: cn("px-2 py-1.5 text-sm font-semibold", className),
|
|
26150
|
-
...props
|
|
26151
|
-
}
|
|
26152
|
-
));
|
|
26153
|
-
SelectLabel.displayName = SelectPrimitive__namespace.Label.displayName;
|
|
26154
|
-
var SelectItem = React23__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26155
|
-
SelectPrimitive__namespace.Item,
|
|
26156
|
-
{
|
|
26157
|
-
ref,
|
|
26158
|
-
className: cn(
|
|
26159
|
-
"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",
|
|
26160
|
-
className
|
|
26161
|
-
),
|
|
26162
|
-
...props,
|
|
26163
|
-
children: [
|
|
26164
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }) }),
|
|
26165
|
-
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
|
|
26166
|
-
]
|
|
26167
|
-
}
|
|
26168
|
-
));
|
|
26169
|
-
SelectItem.displayName = SelectPrimitive__namespace.Item.displayName;
|
|
26170
|
-
var SelectSeparator = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
26171
|
-
SelectPrimitive__namespace.Separator,
|
|
26172
|
-
{
|
|
26173
|
-
ref,
|
|
26174
|
-
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
26175
|
-
...props
|
|
26176
|
-
}
|
|
26177
|
-
));
|
|
26178
|
-
SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
|
|
26179
|
-
var LoadingOverlay = ({
|
|
26180
|
-
isVisible,
|
|
26181
|
-
message = "Loading...",
|
|
26182
|
-
className
|
|
26134
|
+
var PlayPauseIndicator = ({
|
|
26135
|
+
show,
|
|
26136
|
+
isPlaying,
|
|
26137
|
+
duration = 600
|
|
26183
26138
|
}) => {
|
|
26139
|
+
const [isVisible, setIsVisible] = React23.useState(false);
|
|
26140
|
+
const [isFading, setIsFading] = React23.useState(false);
|
|
26141
|
+
React23.useEffect(() => {
|
|
26142
|
+
if (show) {
|
|
26143
|
+
setIsVisible(true);
|
|
26144
|
+
setIsFading(false);
|
|
26145
|
+
const fadeTimer = setTimeout(() => {
|
|
26146
|
+
setIsFading(true);
|
|
26147
|
+
}, 100);
|
|
26148
|
+
const hideTimer = setTimeout(() => {
|
|
26149
|
+
setIsVisible(false);
|
|
26150
|
+
setIsFading(false);
|
|
26151
|
+
}, duration);
|
|
26152
|
+
return () => {
|
|
26153
|
+
clearTimeout(fadeTimer);
|
|
26154
|
+
clearTimeout(hideTimer);
|
|
26155
|
+
};
|
|
26156
|
+
}
|
|
26157
|
+
}, [show, duration]);
|
|
26184
26158
|
if (!isVisible) return null;
|
|
26185
26159
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
26186
|
-
|
|
26160
|
+
"div",
|
|
26187
26161
|
{
|
|
26188
|
-
|
|
26189
|
-
|
|
26190
|
-
|
|
26191
|
-
|
|
26192
|
-
|
|
26193
|
-
"
|
|
26194
|
-
|
|
26195
|
-
|
|
26162
|
+
className: "absolute inset-0 flex items-center justify-center pointer-events-none z-10",
|
|
26163
|
+
style: {
|
|
26164
|
+
opacity: isFading ? 0 : 1,
|
|
26165
|
+
transition: `opacity ${duration - 100}ms ease-out`
|
|
26166
|
+
},
|
|
26167
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-black/70 rounded-full p-6", children: isPlaying ? (
|
|
26168
|
+
// Play icon (triangle)
|
|
26169
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26170
|
+
"svg",
|
|
26171
|
+
{
|
|
26172
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
26173
|
+
viewBox: "0 0 24 24",
|
|
26174
|
+
fill: "white",
|
|
26175
|
+
className: "w-16 h-16",
|
|
26176
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" })
|
|
26177
|
+
}
|
|
26178
|
+
)
|
|
26179
|
+
) : (
|
|
26180
|
+
// Pause icon (two bars)
|
|
26181
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26182
|
+
"svg",
|
|
26183
|
+
{
|
|
26184
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
26185
|
+
viewBox: "0 0 24 24",
|
|
26186
|
+
fill: "white",
|
|
26187
|
+
className: "w-16 h-16",
|
|
26188
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" })
|
|
26189
|
+
}
|
|
26190
|
+
)
|
|
26191
|
+
) })
|
|
26196
26192
|
}
|
|
26197
26193
|
);
|
|
26198
26194
|
};
|
|
26199
|
-
var
|
|
26200
|
-
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
|
|
26204
|
-
|
|
26205
|
-
|
|
26206
|
-
|
|
26207
|
-
|
|
26208
|
-
|
|
26209
|
-
|
|
26210
|
-
|
|
26211
|
-
|
|
26212
|
-
|
|
26213
|
-
|
|
26214
|
-
|
|
26215
|
-
|
|
26216
|
-
|
|
26217
|
-
|
|
26218
|
-
|
|
26219
|
-
|
|
26220
|
-
|
|
26221
|
-
|
|
26222
|
-
|
|
26223
|
-
setTime("Error");
|
|
26224
|
-
}
|
|
26225
|
-
};
|
|
26226
|
-
updateTime();
|
|
26227
|
-
const interval = setInterval(updateTime, 1e3);
|
|
26228
|
-
return () => clearInterval(interval);
|
|
26229
|
-
}, [timezoneToDisplay, dateTimeConfig?.timeFormatOptions, localeToUse]);
|
|
26230
|
-
if (!time2) return null;
|
|
26231
|
-
if (variant === "minimal") {
|
|
26232
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: className || "", children: [
|
|
26233
|
-
time2,
|
|
26234
|
-
" ",
|
|
26235
|
-
timeSuffix
|
|
26236
|
-
] });
|
|
26195
|
+
var ERROR_MAPPING = {
|
|
26196
|
+
"networkError": {
|
|
26197
|
+
code: 2,
|
|
26198
|
+
type: "recoverable",
|
|
26199
|
+
message: "Network error - please check your internet connection",
|
|
26200
|
+
canRetry: true
|
|
26201
|
+
},
|
|
26202
|
+
"mediaError": {
|
|
26203
|
+
code: 3,
|
|
26204
|
+
type: "non_recoverable",
|
|
26205
|
+
message: "Stream corrupted due to internet connection",
|
|
26206
|
+
canRetry: false
|
|
26207
|
+
},
|
|
26208
|
+
"muxError": {
|
|
26209
|
+
code: 3,
|
|
26210
|
+
type: "non_recoverable",
|
|
26211
|
+
message: "Error processing media stream",
|
|
26212
|
+
canRetry: false
|
|
26213
|
+
},
|
|
26214
|
+
"otherError": {
|
|
26215
|
+
code: 4,
|
|
26216
|
+
type: "non_recoverable",
|
|
26217
|
+
message: "Video format not supported by your browser. Please use Google Chrome.",
|
|
26218
|
+
canRetry: false
|
|
26237
26219
|
}
|
|
26238
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26239
|
-
motion.div,
|
|
26240
|
-
{
|
|
26241
|
-
initial: { opacity: 0, y: -5 },
|
|
26242
|
-
animate: { opacity: 1, y: 0 },
|
|
26243
|
-
transition: { duration: 0.3 },
|
|
26244
|
-
className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
|
|
26245
|
-
children: [
|
|
26246
|
-
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
26247
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
|
|
26248
|
-
time2,
|
|
26249
|
-
" ",
|
|
26250
|
-
timeSuffix
|
|
26251
|
-
] })
|
|
26252
|
-
]
|
|
26253
|
-
}
|
|
26254
|
-
);
|
|
26255
26220
|
};
|
|
26256
|
-
var
|
|
26257
|
-
|
|
26258
|
-
|
|
26259
|
-
|
|
26260
|
-
|
|
26261
|
-
|
|
26221
|
+
var hlsVideoPlayerStyles = `
|
|
26222
|
+
.hls-video-player-container {
|
|
26223
|
+
width: 100%;
|
|
26224
|
+
height: 100%;
|
|
26225
|
+
background-color: #000;
|
|
26226
|
+
display: flex;
|
|
26227
|
+
align-items: center;
|
|
26228
|
+
justify-content: center;
|
|
26229
|
+
}
|
|
26230
|
+
|
|
26231
|
+
/* Center the video and maintain aspect ratio */
|
|
26232
|
+
.hls-video-element {
|
|
26233
|
+
width: 100%;
|
|
26234
|
+
height: 100%;
|
|
26235
|
+
max-width: 100%;
|
|
26236
|
+
max-height: 100%;
|
|
26237
|
+
object-fit: contain;
|
|
26238
|
+
}
|
|
26239
|
+
|
|
26240
|
+
/* Custom loading indicator styles */
|
|
26241
|
+
.hls-video-player-loading {
|
|
26242
|
+
position: absolute;
|
|
26243
|
+
top: 50%;
|
|
26244
|
+
left: 50%;
|
|
26245
|
+
transform: translate(-50%, -50%);
|
|
26246
|
+
z-index: 10;
|
|
26247
|
+
}
|
|
26248
|
+
`;
|
|
26249
|
+
if (typeof document !== "undefined") {
|
|
26250
|
+
const styleId = "hls-video-player-custom-styles";
|
|
26251
|
+
if (!document.getElementById(styleId)) {
|
|
26252
|
+
const style = document.createElement("style");
|
|
26253
|
+
style.id = styleId;
|
|
26254
|
+
style.textContent = hlsVideoPlayerStyles;
|
|
26255
|
+
document.head.appendChild(style);
|
|
26256
|
+
}
|
|
26257
|
+
}
|
|
26258
|
+
var BASE_HLS_CONFIG = {
|
|
26259
|
+
maxBufferLength: 3,
|
|
26260
|
+
maxMaxBufferLength: 8,
|
|
26261
|
+
maxBufferSize: 50 * 1e3 * 1e3,
|
|
26262
|
+
maxBufferHole: 0.25,
|
|
26263
|
+
manifestLoadingTimeOut: 15e3,
|
|
26264
|
+
manifestLoadingMaxRetry: 3,
|
|
26265
|
+
manifestLoadingRetryDelay: 500,
|
|
26266
|
+
levelLoadingTimeOut: 6e4,
|
|
26267
|
+
levelLoadingMaxRetry: 5,
|
|
26268
|
+
levelLoadingRetryDelay: 500,
|
|
26269
|
+
fragLoadingTimeOut: 6e4,
|
|
26270
|
+
fragLoadingMaxRetry: 5,
|
|
26271
|
+
fragLoadingRetryDelay: 500,
|
|
26272
|
+
startPosition: -1,
|
|
26273
|
+
debug: false,
|
|
26274
|
+
enableWorker: true,
|
|
26275
|
+
lowLatencyMode: false,
|
|
26276
|
+
progressive: true,
|
|
26277
|
+
abrEwmaSlowLive: 9,
|
|
26278
|
+
abrEwmaFastLive: 3,
|
|
26279
|
+
abrBandWidthFactor: 0.95,
|
|
26280
|
+
abrBandWidthUpFactor: 0.7,
|
|
26281
|
+
abrMaxWithRealBitrate: false,
|
|
26282
|
+
abrEwmaDefaultEstimate: 5e7
|
|
26283
|
+
};
|
|
26284
|
+
var HlsVideoPlayer = React23.forwardRef(({
|
|
26285
|
+
src,
|
|
26286
|
+
poster,
|
|
26287
|
+
autoplay = false,
|
|
26288
|
+
controls = true,
|
|
26289
|
+
loop = false,
|
|
26290
|
+
muted = false,
|
|
26291
|
+
playsInline = true,
|
|
26292
|
+
className = "",
|
|
26293
|
+
hlsConfig = {},
|
|
26294
|
+
options = {},
|
|
26295
|
+
// Backward compatibility with Video.js
|
|
26296
|
+
externalLoadingControl = false,
|
|
26297
|
+
onLoadingChange,
|
|
26298
|
+
onReady,
|
|
26299
|
+
onPlay,
|
|
26300
|
+
onPause,
|
|
26301
|
+
onPlaying,
|
|
26302
|
+
onTimeUpdate,
|
|
26303
|
+
onDurationChange,
|
|
26304
|
+
onEnded,
|
|
26305
|
+
onError,
|
|
26306
|
+
onLoadStart,
|
|
26307
|
+
onLoadedMetadata,
|
|
26308
|
+
onLoadedData,
|
|
26309
|
+
onSeeking,
|
|
26310
|
+
onSeeked,
|
|
26311
|
+
onClick
|
|
26312
|
+
}, ref) => {
|
|
26313
|
+
const videoContainerRef = React23.useRef(null);
|
|
26314
|
+
const videoRef = React23.useRef(null);
|
|
26315
|
+
const hlsRef = React23.useRef(null);
|
|
26316
|
+
const blobUrlRef = React23.useRef(null);
|
|
26317
|
+
const [isReady, setIsReady] = React23.useState(false);
|
|
26318
|
+
const [isLoading, setIsLoading] = React23.useState(true);
|
|
26319
|
+
const [showIndicator, setShowIndicator] = React23.useState(false);
|
|
26320
|
+
const [indicatorIsPlaying, setIndicatorIsPlaying] = React23.useState(false);
|
|
26321
|
+
const indicatorKeyRef = React23.useRef(0);
|
|
26322
|
+
const eventCallbacksRef = React23.useRef({
|
|
26323
|
+
onReady,
|
|
26324
|
+
onPlay,
|
|
26325
|
+
onPause,
|
|
26326
|
+
onPlaying,
|
|
26327
|
+
onTimeUpdate,
|
|
26328
|
+
onDurationChange,
|
|
26329
|
+
onEnded,
|
|
26330
|
+
onError,
|
|
26331
|
+
onLoadStart,
|
|
26332
|
+
onLoadedMetadata,
|
|
26333
|
+
onLoadedData,
|
|
26334
|
+
onSeeking,
|
|
26335
|
+
onSeeked,
|
|
26336
|
+
onLoadingChange
|
|
26337
|
+
});
|
|
26262
26338
|
React23.useEffect(() => {
|
|
26263
|
-
|
|
26264
|
-
|
|
26265
|
-
|
|
26266
|
-
|
|
26267
|
-
|
|
26268
|
-
|
|
26269
|
-
|
|
26270
|
-
|
|
26271
|
-
|
|
26272
|
-
|
|
26273
|
-
|
|
26274
|
-
|
|
26275
|
-
|
|
26276
|
-
|
|
26277
|
-
|
|
26278
|
-
}
|
|
26339
|
+
eventCallbacksRef.current = {
|
|
26340
|
+
onReady,
|
|
26341
|
+
onPlay,
|
|
26342
|
+
onPause,
|
|
26343
|
+
onPlaying,
|
|
26344
|
+
onTimeUpdate,
|
|
26345
|
+
onDurationChange,
|
|
26346
|
+
onEnded,
|
|
26347
|
+
onError,
|
|
26348
|
+
onLoadStart,
|
|
26349
|
+
onLoadedMetadata,
|
|
26350
|
+
onLoadedData,
|
|
26351
|
+
onSeeking,
|
|
26352
|
+
onSeeked,
|
|
26353
|
+
onLoadingChange
|
|
26279
26354
|
};
|
|
26280
|
-
|
|
26281
|
-
|
|
26355
|
+
}, [
|
|
26356
|
+
onReady,
|
|
26357
|
+
onPlay,
|
|
26358
|
+
onPause,
|
|
26359
|
+
onPlaying,
|
|
26360
|
+
onTimeUpdate,
|
|
26361
|
+
onDurationChange,
|
|
26362
|
+
onEnded,
|
|
26363
|
+
onError,
|
|
26364
|
+
onLoadStart,
|
|
26365
|
+
onLoadedMetadata,
|
|
26366
|
+
onLoadedData,
|
|
26367
|
+
onSeeking,
|
|
26368
|
+
onSeeked,
|
|
26369
|
+
onLoadingChange
|
|
26370
|
+
]);
|
|
26371
|
+
const stableHlsConfigRef = React23.useRef(hlsConfig);
|
|
26372
|
+
const stableOptionsRef = React23.useRef(options);
|
|
26373
|
+
const configSignatureRef = React23.useRef("");
|
|
26374
|
+
const [configVersion, setConfigVersion] = React23.useState(0);
|
|
26375
|
+
React23.useEffect(() => {
|
|
26376
|
+
const serialized = JSON.stringify({
|
|
26377
|
+
hlsConfig: hlsConfig || null,
|
|
26378
|
+
options: options || null
|
|
26379
|
+
});
|
|
26380
|
+
if (!configSignatureRef.current) {
|
|
26381
|
+
configSignatureRef.current = serialized;
|
|
26382
|
+
stableHlsConfigRef.current = hlsConfig;
|
|
26383
|
+
stableOptionsRef.current = options;
|
|
26384
|
+
return;
|
|
26385
|
+
}
|
|
26386
|
+
if (configSignatureRef.current !== serialized) {
|
|
26387
|
+
configSignatureRef.current = serialized;
|
|
26388
|
+
stableHlsConfigRef.current = hlsConfig;
|
|
26389
|
+
stableOptionsRef.current = options;
|
|
26390
|
+
setConfigVersion((prev) => prev + 1);
|
|
26391
|
+
}
|
|
26392
|
+
}, [hlsConfig, options]);
|
|
26393
|
+
const cleanupBlobUrl = React23.useCallback(() => {
|
|
26394
|
+
if (blobUrlRef.current) {
|
|
26395
|
+
URL.revokeObjectURL(blobUrlRef.current);
|
|
26396
|
+
blobUrlRef.current = null;
|
|
26397
|
+
}
|
|
26398
|
+
}, []);
|
|
26399
|
+
const dispose = React23.useCallback(() => {
|
|
26400
|
+
if (hlsRef.current) {
|
|
26401
|
+
hlsRef.current.destroy();
|
|
26402
|
+
hlsRef.current = null;
|
|
26403
|
+
}
|
|
26404
|
+
cleanupBlobUrl();
|
|
26405
|
+
setIsReady(false);
|
|
26406
|
+
}, [cleanupBlobUrl]);
|
|
26407
|
+
const playerLikeObject = React23.useCallback(() => {
|
|
26408
|
+
return {
|
|
26409
|
+
el: () => videoRef.current,
|
|
26410
|
+
currentTime: () => videoRef.current?.currentTime || 0,
|
|
26411
|
+
duration: () => videoRef.current?.duration || 0,
|
|
26412
|
+
paused: () => videoRef.current?.paused ?? true,
|
|
26413
|
+
play: () => videoRef.current?.play(),
|
|
26414
|
+
pause: () => videoRef.current?.pause(),
|
|
26415
|
+
muted: (val) => {
|
|
26416
|
+
if (videoRef.current) {
|
|
26417
|
+
if (val !== void 0) videoRef.current.muted = val;
|
|
26418
|
+
return videoRef.current.muted;
|
|
26419
|
+
}
|
|
26420
|
+
return false;
|
|
26421
|
+
},
|
|
26422
|
+
volume: (val) => {
|
|
26423
|
+
if (videoRef.current) {
|
|
26424
|
+
if (val !== void 0) videoRef.current.volume = val;
|
|
26425
|
+
return videoRef.current.volume;
|
|
26426
|
+
}
|
|
26427
|
+
return 1;
|
|
26428
|
+
},
|
|
26429
|
+
error: () => null,
|
|
26430
|
+
dispose: () => dispose()
|
|
26282
26431
|
};
|
|
26283
|
-
|
|
26284
|
-
|
|
26285
|
-
|
|
26286
|
-
|
|
26287
|
-
|
|
26432
|
+
}, [dispose]);
|
|
26433
|
+
const initializePlayer = React23.useCallback(() => {
|
|
26434
|
+
if (!videoRef.current || !src) return;
|
|
26435
|
+
const video = videoRef.current;
|
|
26436
|
+
const player = playerLikeObject();
|
|
26437
|
+
const mergedHlsConfig = {
|
|
26438
|
+
...BASE_HLS_CONFIG,
|
|
26439
|
+
...stableHlsConfigRef.current || {},
|
|
26440
|
+
...stableOptionsRef.current || {}
|
|
26441
|
+
};
|
|
26442
|
+
cleanupBlobUrl();
|
|
26443
|
+
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
26444
|
+
if (isHLS) {
|
|
26445
|
+
if (src.startsWith("#EXTM3U")) {
|
|
26446
|
+
const safariMode = isSafari();
|
|
26447
|
+
const browserName = getBrowserName();
|
|
26448
|
+
if (safariMode) {
|
|
26449
|
+
const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
|
|
26450
|
+
if (clipIdMatch) {
|
|
26451
|
+
const proxyUrl = `/api/clips/playlist/${clipIdMatch[1]}`;
|
|
26452
|
+
console.log(`[HlsVideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, proxyUrl);
|
|
26453
|
+
video.src = proxyUrl;
|
|
26454
|
+
} else {
|
|
26455
|
+
console.warn("[HlsVideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
|
|
26456
|
+
const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
|
|
26457
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
26458
|
+
blobUrlRef.current = blobUrl;
|
|
26459
|
+
video.src = blobUrl;
|
|
26460
|
+
}
|
|
26461
|
+
} else if (Hls3__default.default.isSupported()) {
|
|
26462
|
+
const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
|
|
26463
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
26464
|
+
blobUrlRef.current = blobUrl;
|
|
26465
|
+
console.log(`[HlsVideoPlayer] Non-Safari browser (${browserName}) - using HLS.js with blob URL`);
|
|
26466
|
+
const hls = new Hls3__default.default(mergedHlsConfig);
|
|
26467
|
+
hlsRef.current = hls;
|
|
26468
|
+
hls.on(Hls3.Events.MANIFEST_PARSED, () => {
|
|
26469
|
+
console.log("[HlsVideoPlayer] Manifest parsed, ready to play");
|
|
26470
|
+
setIsReady(true);
|
|
26471
|
+
eventCallbacksRef.current.onReady?.(player);
|
|
26472
|
+
});
|
|
26473
|
+
hls.on(Hls3.Events.ERROR, (event, data) => {
|
|
26474
|
+
console.error("[HlsVideoPlayer] HLS.js error:", data);
|
|
26475
|
+
if (data.fatal) {
|
|
26476
|
+
let errorInfo;
|
|
26477
|
+
switch (data.type) {
|
|
26478
|
+
case Hls3.ErrorTypes.NETWORK_ERROR:
|
|
26479
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
26480
|
+
console.log("[HlsVideoPlayer] Attempting to recover from network error");
|
|
26481
|
+
hls.startLoad();
|
|
26482
|
+
break;
|
|
26483
|
+
case Hls3.ErrorTypes.MEDIA_ERROR:
|
|
26484
|
+
errorInfo = ERROR_MAPPING.mediaError;
|
|
26485
|
+
console.log("[HlsVideoPlayer] Attempting to recover from media error");
|
|
26486
|
+
hls.recoverMediaError();
|
|
26487
|
+
break;
|
|
26488
|
+
case Hls3.ErrorTypes.MUX_ERROR:
|
|
26489
|
+
errorInfo = ERROR_MAPPING.muxError;
|
|
26490
|
+
break;
|
|
26491
|
+
default:
|
|
26492
|
+
errorInfo = ERROR_MAPPING.otherError;
|
|
26493
|
+
break;
|
|
26494
|
+
}
|
|
26495
|
+
errorInfo.details = data.details;
|
|
26496
|
+
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
26497
|
+
}
|
|
26498
|
+
});
|
|
26499
|
+
hls.on(Hls3.Events.FRAG_LOADING, () => {
|
|
26500
|
+
setIsLoading(true);
|
|
26501
|
+
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
26502
|
+
});
|
|
26503
|
+
hls.on(Hls3.Events.FRAG_LOADED, () => {
|
|
26504
|
+
setIsLoading(false);
|
|
26505
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26506
|
+
});
|
|
26507
|
+
hls.loadSource(blobUrl);
|
|
26508
|
+
hls.attachMedia(video);
|
|
26509
|
+
} else {
|
|
26510
|
+
console.error("[HlsVideoPlayer] HLS.js not supported and not Safari - cannot play HLS content");
|
|
26511
|
+
const errorInfo = ERROR_MAPPING.otherError;
|
|
26512
|
+
onError?.(player, errorInfo);
|
|
26513
|
+
}
|
|
26514
|
+
} else {
|
|
26515
|
+
if (Hls3__default.default.isSupported() && !isSafari()) {
|
|
26516
|
+
const hls = new Hls3__default.default(mergedHlsConfig);
|
|
26517
|
+
hlsRef.current = hls;
|
|
26518
|
+
hls.on(Hls3.Events.MANIFEST_PARSED, () => {
|
|
26519
|
+
setIsReady(true);
|
|
26520
|
+
eventCallbacksRef.current.onReady?.(player);
|
|
26521
|
+
});
|
|
26522
|
+
hls.on(Hls3.Events.ERROR, (event, data) => {
|
|
26523
|
+
console.error("[HlsVideoPlayer] HLS.js error:", data);
|
|
26524
|
+
if (data.fatal) {
|
|
26525
|
+
let errorInfo;
|
|
26526
|
+
switch (data.type) {
|
|
26527
|
+
case Hls3.ErrorTypes.NETWORK_ERROR:
|
|
26528
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
26529
|
+
hls.startLoad();
|
|
26530
|
+
break;
|
|
26531
|
+
case Hls3.ErrorTypes.MEDIA_ERROR:
|
|
26532
|
+
errorInfo = ERROR_MAPPING.mediaError;
|
|
26533
|
+
hls.recoverMediaError();
|
|
26534
|
+
break;
|
|
26535
|
+
default:
|
|
26536
|
+
errorInfo = ERROR_MAPPING.otherError;
|
|
26537
|
+
break;
|
|
26538
|
+
}
|
|
26539
|
+
errorInfo.details = data.details;
|
|
26540
|
+
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
26541
|
+
}
|
|
26542
|
+
});
|
|
26543
|
+
hls.loadSource(src);
|
|
26544
|
+
hls.attachMedia(video);
|
|
26545
|
+
} else {
|
|
26546
|
+
video.src = src;
|
|
26547
|
+
}
|
|
26288
26548
|
}
|
|
26289
|
-
}
|
|
26290
|
-
|
|
26291
|
-
}, [date, timezoneToDisplay, dateTimeConfig?.dateFormatOptions, localeToUse]);
|
|
26292
|
-
if (!date) return null;
|
|
26293
|
-
if (variant === "minimal") {
|
|
26294
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: className || "", children: date });
|
|
26295
|
-
}
|
|
26296
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
26297
|
-
motion.div,
|
|
26298
|
-
{
|
|
26299
|
-
initial: { opacity: 0, y: -5 },
|
|
26300
|
-
animate: { opacity: 1, y: 0 },
|
|
26301
|
-
transition: { duration: 0.3 },
|
|
26302
|
-
className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
|
|
26303
|
-
children: [
|
|
26304
|
-
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
26305
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-gray-800 tracking-tight", children: date })
|
|
26306
|
-
]
|
|
26549
|
+
} else {
|
|
26550
|
+
video.src = src;
|
|
26307
26551
|
}
|
|
26308
|
-
|
|
26309
|
-
|
|
26310
|
-
|
|
26311
|
-
|
|
26312
|
-
var CardHeader3 = CardHeader2;
|
|
26313
|
-
var CardTitle3 = CardTitle2;
|
|
26314
|
-
var CardContent3 = CardContent2;
|
|
26315
|
-
var MetricCard2 = ({ title, value, unit = "", trend = null }) => {
|
|
26316
|
-
const getTrendColor = (trendValue) => {
|
|
26317
|
-
if (trendValue === null || trendValue === void 0) return "";
|
|
26318
|
-
return trendValue > 0 ? "text-green-500" : trendValue < 0 ? "text-red-500" : "text-gray-500";
|
|
26319
|
-
};
|
|
26320
|
-
const getTrendSymbol = (trendValue) => {
|
|
26321
|
-
if (trendValue === null || trendValue === void 0) return "";
|
|
26322
|
-
return trendValue > 0 ? "\u2191" : trendValue < 0 ? "\u2193" : "";
|
|
26323
|
-
};
|
|
26324
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(Card3, { className: "bg-white", children: [
|
|
26325
|
-
/* @__PURE__ */ jsxRuntime.jsx(CardHeader3, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle3, { className: "text-sm font-medium text-gray-500", children: title }) }),
|
|
26326
|
-
/* @__PURE__ */ jsxRuntime.jsx(CardContent3, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
26327
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-semibold", children: [
|
|
26328
|
-
value,
|
|
26329
|
-
unit && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-sm text-gray-500", children: unit })
|
|
26330
|
-
] }),
|
|
26331
|
-
trend !== null && trend !== void 0 && // Check trend for null/undefined before accessing
|
|
26332
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center ${getTrendColor(trend)}`, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm", children: [
|
|
26333
|
-
getTrendSymbol(trend),
|
|
26334
|
-
" ",
|
|
26335
|
-
Math.abs(trend),
|
|
26336
|
-
"%"
|
|
26337
|
-
] }) })
|
|
26338
|
-
] }) })
|
|
26339
|
-
] });
|
|
26340
|
-
};
|
|
26341
|
-
var MetricCard_default = MetricCard2;
|
|
26342
|
-
var TimePickerDropdown = ({
|
|
26343
|
-
value,
|
|
26344
|
-
onChange,
|
|
26345
|
-
placeholder = "Select time",
|
|
26346
|
-
className = "",
|
|
26347
|
-
disabled = false
|
|
26348
|
-
}) => {
|
|
26349
|
-
const [isOpen, setIsOpen] = React23.useState(false);
|
|
26350
|
-
const [searchTerm, setSearchTerm] = React23.useState("");
|
|
26351
|
-
const dropdownRef = React23.useRef(null);
|
|
26352
|
-
const inputRef = React23.useRef(null);
|
|
26353
|
-
const generateTimeSlots = () => {
|
|
26354
|
-
const slots = [];
|
|
26355
|
-
for (let hour = 0; hour < 24; hour++) {
|
|
26356
|
-
for (let minute = 0; minute < 60; minute += 15) {
|
|
26357
|
-
const time24 = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
|
|
26358
|
-
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
26359
|
-
const ampm = hour < 12 ? "AM" : "PM";
|
|
26360
|
-
const time12 = `${hour12}:${minute.toString().padStart(2, "0")} ${ampm}`;
|
|
26361
|
-
slots.push({ value: time24, label: time12 });
|
|
26362
|
-
}
|
|
26363
|
-
}
|
|
26364
|
-
return slots;
|
|
26365
|
-
};
|
|
26366
|
-
const timeSlots = generateTimeSlots();
|
|
26367
|
-
const filteredSlots = timeSlots.filter(
|
|
26368
|
-
(slot) => slot.label.toLowerCase().includes(searchTerm.toLowerCase())
|
|
26369
|
-
);
|
|
26370
|
-
const getDisplayValue = (value2) => {
|
|
26371
|
-
if (!value2) return "";
|
|
26372
|
-
const normalizedValue = value2.substring(0, 5);
|
|
26373
|
-
const slot = timeSlots.find((s) => s.value === normalizedValue);
|
|
26374
|
-
return slot ? slot.label : value2;
|
|
26375
|
-
};
|
|
26376
|
-
React23.useEffect(() => {
|
|
26377
|
-
const handleClickOutside = (event) => {
|
|
26378
|
-
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
26379
|
-
setIsOpen(false);
|
|
26380
|
-
setSearchTerm("");
|
|
26552
|
+
const handleCanPlay = () => {
|
|
26553
|
+
if (!hlsRef.current) {
|
|
26554
|
+
setIsReady(true);
|
|
26555
|
+
onReady?.(player);
|
|
26381
26556
|
}
|
|
26382
26557
|
};
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
setSearchTerm("");
|
|
26390
|
-
} else if (e.key === "Enter") {
|
|
26391
|
-
e.preventDefault();
|
|
26392
|
-
if (filteredSlots.length > 0) {
|
|
26393
|
-
onChange(filteredSlots[0].value);
|
|
26394
|
-
setIsOpen(false);
|
|
26395
|
-
setSearchTerm("");
|
|
26396
|
-
}
|
|
26397
|
-
}
|
|
26398
|
-
};
|
|
26399
|
-
const handleSelect = (timeValue) => {
|
|
26400
|
-
onChange(timeValue);
|
|
26401
|
-
setIsOpen(false);
|
|
26402
|
-
setSearchTerm("");
|
|
26403
|
-
};
|
|
26404
|
-
const handleToggle = () => {
|
|
26405
|
-
if (disabled) return;
|
|
26406
|
-
setIsOpen(!isOpen);
|
|
26407
|
-
if (!isOpen) {
|
|
26408
|
-
setTimeout(() => inputRef.current?.focus(), 100);
|
|
26409
|
-
}
|
|
26410
|
-
};
|
|
26411
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [
|
|
26412
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26413
|
-
"button",
|
|
26414
|
-
{
|
|
26415
|
-
type: "button",
|
|
26416
|
-
onClick: handleToggle,
|
|
26417
|
-
disabled,
|
|
26418
|
-
className: `
|
|
26419
|
-
w-full px-3 py-2 text-left bg-white border border-gray-300 rounded-md shadow-sm
|
|
26420
|
-
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
|
|
26421
|
-
hover:border-gray-400 transition-colors duration-200
|
|
26422
|
-
${disabled ? "bg-gray-50 cursor-not-allowed" : "cursor-pointer"}
|
|
26423
|
-
${isOpen ? "ring-2 ring-blue-500 border-blue-500" : ""}
|
|
26424
|
-
`,
|
|
26425
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
26426
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
26427
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-4 w-4 text-gray-400" }),
|
|
26428
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-sm ${value ? "text-gray-900" : "text-gray-500"}`, children: value ? getDisplayValue(value) : placeholder })
|
|
26429
|
-
] }),
|
|
26430
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26431
|
-
lucideReact.ChevronDown,
|
|
26432
|
-
{
|
|
26433
|
-
className: `h-4 w-4 text-gray-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
|
|
26434
|
-
}
|
|
26435
|
-
)
|
|
26436
|
-
] })
|
|
26437
|
-
}
|
|
26438
|
-
),
|
|
26439
|
-
isOpen && /* @__PURE__ */ jsxRuntime.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: [
|
|
26440
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
26441
|
-
"input",
|
|
26442
|
-
{
|
|
26443
|
-
ref: inputRef,
|
|
26444
|
-
type: "text",
|
|
26445
|
-
placeholder: "Search time...",
|
|
26446
|
-
value: searchTerm,
|
|
26447
|
-
onChange: (e) => setSearchTerm(e.target.value),
|
|
26448
|
-
onKeyDown: handleKeyDown,
|
|
26449
|
-
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"
|
|
26450
|
-
}
|
|
26451
|
-
) }),
|
|
26452
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-y-auto max-h-48", children: filteredSlots.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: "No times found" }) : filteredSlots.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
26453
|
-
"button",
|
|
26454
|
-
{
|
|
26455
|
-
type: "button",
|
|
26456
|
-
onClick: () => handleSelect(slot.value),
|
|
26457
|
-
className: `
|
|
26458
|
-
w-full px-3 py-2 text-left text-sm hover:bg-blue-50 hover:text-blue-600
|
|
26459
|
-
transition-colors duration-150 border-b border-gray-100 last:border-b-0
|
|
26460
|
-
${slot.value === value ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-700"}
|
|
26461
|
-
`,
|
|
26462
|
-
children: slot.label
|
|
26463
|
-
},
|
|
26464
|
-
slot.value
|
|
26465
|
-
)) })
|
|
26466
|
-
] })
|
|
26467
|
-
] });
|
|
26468
|
-
};
|
|
26469
|
-
var SilentErrorBoundary = class extends React23__namespace.default.Component {
|
|
26470
|
-
constructor(props) {
|
|
26471
|
-
super(props);
|
|
26472
|
-
this.handleClearAndReload = () => {
|
|
26473
|
-
console.log("[ErrorBoundary] User initiated reset");
|
|
26474
|
-
if (typeof window !== "undefined") {
|
|
26475
|
-
try {
|
|
26476
|
-
localStorage.clear();
|
|
26477
|
-
sessionStorage.clear();
|
|
26478
|
-
console.log("[ErrorBoundary] Cleared all storage");
|
|
26479
|
-
} catch (error) {
|
|
26480
|
-
console.error("[ErrorBoundary] Failed to clear storage:", error);
|
|
26481
|
-
}
|
|
26482
|
-
}
|
|
26483
|
-
window.location.href = "/login";
|
|
26558
|
+
const handlePlay = () => eventCallbacksRef.current.onPlay?.(player);
|
|
26559
|
+
const handlePause = () => eventCallbacksRef.current.onPause?.(player);
|
|
26560
|
+
const handlePlaying = () => {
|
|
26561
|
+
setIsLoading(false);
|
|
26562
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26563
|
+
eventCallbacksRef.current.onPlaying?.(player);
|
|
26484
26564
|
};
|
|
26485
|
-
|
|
26486
|
-
|
|
26487
|
-
|
|
26488
|
-
lastError: null,
|
|
26489
|
-
errorInfo: null
|
|
26565
|
+
const handleTimeUpdate = () => {
|
|
26566
|
+
const currentTime2 = video.currentTime || 0;
|
|
26567
|
+
eventCallbacksRef.current.onTimeUpdate?.(player, currentTime2);
|
|
26490
26568
|
};
|
|
26491
|
-
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26496
|
-
|
|
26497
|
-
error: error.message,
|
|
26498
|
-
stack: error.stack,
|
|
26499
|
-
componentStack: errorInfo.componentStack,
|
|
26500
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
26501
|
-
});
|
|
26502
|
-
this.setState((prev) => ({
|
|
26503
|
-
errorCount: prev.errorCount + 1,
|
|
26504
|
-
lastError: error,
|
|
26505
|
-
errorInfo
|
|
26506
|
-
}));
|
|
26507
|
-
try {
|
|
26508
|
-
if (typeof window !== "undefined" && window.mixpanel) {
|
|
26509
|
-
window.mixpanel.track("React Render Error", {
|
|
26510
|
-
error: error.message,
|
|
26511
|
-
component: errorInfo.componentStack?.split("\n")[1] || "unknown",
|
|
26512
|
-
errorCount: this.state.errorCount + 1
|
|
26513
|
-
});
|
|
26514
|
-
}
|
|
26515
|
-
} catch (analyticsError) {
|
|
26516
|
-
console.warn("[ErrorBoundary] Analytics tracking failed:", analyticsError);
|
|
26517
|
-
}
|
|
26518
|
-
}
|
|
26519
|
-
render() {
|
|
26520
|
-
if (!this.state.hasError) {
|
|
26521
|
-
return this.props.children;
|
|
26522
|
-
}
|
|
26523
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen w-screen items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center space-y-6 text-center", children: [
|
|
26524
|
-
/* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading Dashboard..." }),
|
|
26525
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Taking longer than usual..." }),
|
|
26526
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
26527
|
-
"a",
|
|
26528
|
-
{
|
|
26529
|
-
href: "#",
|
|
26530
|
-
onClick: (e) => {
|
|
26531
|
-
e.preventDefault();
|
|
26532
|
-
this.handleClearAndReload();
|
|
26533
|
-
},
|
|
26534
|
-
className: "text-xs text-gray-400 hover:text-gray-600 underline transition-colors",
|
|
26535
|
-
children: "Reset and try again"
|
|
26536
|
-
}
|
|
26537
|
-
) }),
|
|
26538
|
-
process.env.NODE_ENV === "development" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-400 italic mt-4", children: "Check console for error details" })
|
|
26539
|
-
] }) });
|
|
26540
|
-
}
|
|
26541
|
-
};
|
|
26542
|
-
var PlayPauseIndicator = ({
|
|
26543
|
-
show,
|
|
26544
|
-
isPlaying,
|
|
26545
|
-
duration = 600
|
|
26546
|
-
}) => {
|
|
26547
|
-
const [isVisible, setIsVisible] = React23.useState(false);
|
|
26548
|
-
const [isFading, setIsFading] = React23.useState(false);
|
|
26549
|
-
React23.useEffect(() => {
|
|
26550
|
-
if (show) {
|
|
26551
|
-
setIsVisible(true);
|
|
26552
|
-
setIsFading(false);
|
|
26553
|
-
const fadeTimer = setTimeout(() => {
|
|
26554
|
-
setIsFading(true);
|
|
26555
|
-
}, 100);
|
|
26556
|
-
const hideTimer = setTimeout(() => {
|
|
26557
|
-
setIsVisible(false);
|
|
26558
|
-
setIsFading(false);
|
|
26559
|
-
}, duration);
|
|
26560
|
-
return () => {
|
|
26561
|
-
clearTimeout(fadeTimer);
|
|
26562
|
-
clearTimeout(hideTimer);
|
|
26563
|
-
};
|
|
26564
|
-
}
|
|
26565
|
-
}, [show, duration]);
|
|
26566
|
-
if (!isVisible) return null;
|
|
26567
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
26568
|
-
"div",
|
|
26569
|
-
{
|
|
26570
|
-
className: "absolute inset-0 flex items-center justify-center pointer-events-none z-10",
|
|
26571
|
-
style: {
|
|
26572
|
-
opacity: isFading ? 0 : 1,
|
|
26573
|
-
transition: `opacity ${duration - 100}ms ease-out`
|
|
26574
|
-
},
|
|
26575
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-black/70 rounded-full p-6", children: isPlaying ? (
|
|
26576
|
-
// Play icon (triangle)
|
|
26577
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26578
|
-
"svg",
|
|
26579
|
-
{
|
|
26580
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
26581
|
-
viewBox: "0 0 24 24",
|
|
26582
|
-
fill: "white",
|
|
26583
|
-
className: "w-16 h-16",
|
|
26584
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 5v14l11-7z" })
|
|
26585
|
-
}
|
|
26586
|
-
)
|
|
26587
|
-
) : (
|
|
26588
|
-
// Pause icon (two bars)
|
|
26589
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
26590
|
-
"svg",
|
|
26591
|
-
{
|
|
26592
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
26593
|
-
viewBox: "0 0 24 24",
|
|
26594
|
-
fill: "white",
|
|
26595
|
-
className: "w-16 h-16",
|
|
26596
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 4h4v16H6V4zm8 0h4v16h-4V4z" })
|
|
26597
|
-
}
|
|
26598
|
-
)
|
|
26599
|
-
) })
|
|
26600
|
-
}
|
|
26601
|
-
);
|
|
26602
|
-
};
|
|
26603
|
-
var ERROR_MAPPING = {
|
|
26604
|
-
1: {
|
|
26605
|
-
// MEDIA_ERR_ABORTED
|
|
26606
|
-
code: 1,
|
|
26607
|
-
type: "recoverable" /* RECOVERABLE */,
|
|
26608
|
-
message: "Video loading was interrupted",
|
|
26609
|
-
canRetry: true
|
|
26610
|
-
},
|
|
26611
|
-
2: {
|
|
26612
|
-
// MEDIA_ERR_NETWORK
|
|
26613
|
-
code: 2,
|
|
26614
|
-
type: "recoverable" /* RECOVERABLE */,
|
|
26615
|
-
message: "Network error - please check your internet connection",
|
|
26616
|
-
canRetry: true
|
|
26617
|
-
},
|
|
26618
|
-
3: {
|
|
26619
|
-
// MEDIA_ERR_DECODE
|
|
26620
|
-
code: 3,
|
|
26621
|
-
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
26622
|
-
message: "Stream corrupted due to internet connection",
|
|
26623
|
-
canRetry: false
|
|
26624
|
-
},
|
|
26625
|
-
4: {
|
|
26626
|
-
// MEDIA_ERR_SRC_NOT_SUPPORTED
|
|
26627
|
-
code: 4,
|
|
26628
|
-
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
26629
|
-
message: "Video format not supported by your browser. Please use Google Chrome.",
|
|
26630
|
-
canRetry: false
|
|
26631
|
-
}
|
|
26632
|
-
};
|
|
26633
|
-
var videoPlayerStyles = `
|
|
26634
|
-
.video-player-container {
|
|
26635
|
-
width: 100%;
|
|
26636
|
-
height: 100%;
|
|
26637
|
-
background-color: #000;
|
|
26638
|
-
display: flex;
|
|
26639
|
-
align-items: center;
|
|
26640
|
-
justify-content: center;
|
|
26641
|
-
}
|
|
26642
|
-
|
|
26643
|
-
/* Center the video player and maintain aspect ratio */
|
|
26644
|
-
.video-js {
|
|
26645
|
-
width: 100%;
|
|
26646
|
-
height: 100%;
|
|
26647
|
-
max-width: 100%;
|
|
26648
|
-
max-height: 100%;
|
|
26649
|
-
}
|
|
26650
|
-
|
|
26651
|
-
/* Ensure video maintains aspect ratio and shows fully */
|
|
26652
|
-
.video-js .vjs-tech {
|
|
26653
|
-
position: absolute;
|
|
26654
|
-
top: 0;
|
|
26655
|
-
left: 0;
|
|
26656
|
-
width: 100%;
|
|
26657
|
-
height: 100%;
|
|
26658
|
-
object-fit: contain;
|
|
26659
|
-
}
|
|
26660
|
-
|
|
26661
|
-
/* Hide default Video.js loading spinner */
|
|
26662
|
-
.video-js .vjs-loading-spinner {
|
|
26663
|
-
display: none !important;
|
|
26664
|
-
}
|
|
26665
|
-
|
|
26666
|
-
/* Custom loading indicator styles */
|
|
26667
|
-
.video-player-loading {
|
|
26668
|
-
position: absolute;
|
|
26669
|
-
top: 50%;
|
|
26670
|
-
left: 50%;
|
|
26671
|
-
transform: translate(-50%, -50%);
|
|
26672
|
-
z-index: 10;
|
|
26673
|
-
}
|
|
26674
|
-
`;
|
|
26675
|
-
if (typeof document !== "undefined") {
|
|
26676
|
-
const styleId = "video-player-custom-styles";
|
|
26677
|
-
if (!document.getElementById(styleId)) {
|
|
26678
|
-
const style = document.createElement("style");
|
|
26679
|
-
style.id = styleId;
|
|
26680
|
-
style.textContent = videoPlayerStyles;
|
|
26681
|
-
document.head.appendChild(style);
|
|
26682
|
-
}
|
|
26683
|
-
}
|
|
26684
|
-
var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
26685
|
-
src,
|
|
26686
|
-
poster,
|
|
26687
|
-
autoplay = false,
|
|
26688
|
-
controls = true,
|
|
26689
|
-
loop = false,
|
|
26690
|
-
muted = false,
|
|
26691
|
-
playsInline = true,
|
|
26692
|
-
className = "",
|
|
26693
|
-
options = {},
|
|
26694
|
-
externalLoadingControl = false,
|
|
26695
|
-
onLoadingChange,
|
|
26696
|
-
onReady,
|
|
26697
|
-
onPlay,
|
|
26698
|
-
onPause,
|
|
26699
|
-
onPlaying,
|
|
26700
|
-
onTimeUpdate,
|
|
26701
|
-
onDurationChange,
|
|
26702
|
-
onEnded,
|
|
26703
|
-
onError,
|
|
26704
|
-
onLoadStart,
|
|
26705
|
-
onLoadedMetadata,
|
|
26706
|
-
onLoadedData,
|
|
26707
|
-
onSeeking,
|
|
26708
|
-
onSeeked,
|
|
26709
|
-
onClick
|
|
26710
|
-
}, ref) => {
|
|
26711
|
-
const videoRef = React23.useRef(null);
|
|
26712
|
-
const playerRef = React23.useRef(null);
|
|
26713
|
-
const [isReady, setIsReady] = React23.useState(false);
|
|
26714
|
-
const [isLoading, setIsLoading] = React23.useState(true);
|
|
26715
|
-
const [showIndicator, setShowIndicator] = React23.useState(false);
|
|
26716
|
-
const [indicatorIsPlaying, setIndicatorIsPlaying] = React23.useState(false);
|
|
26717
|
-
const indicatorKeyRef = React23.useRef(0);
|
|
26718
|
-
const defaultOptions = {
|
|
26719
|
-
controls: false,
|
|
26720
|
-
// Always disable Video.js controls - we use custom controls
|
|
26721
|
-
// Don't use fluid or fill - let the video maintain its natural aspect ratio
|
|
26722
|
-
fluid: false,
|
|
26723
|
-
// Disable fluid mode to prevent stretching
|
|
26724
|
-
responsive: false,
|
|
26725
|
-
// Disable responsive mode
|
|
26726
|
-
fill: false,
|
|
26727
|
-
// Don't fill - maintain aspect ratio
|
|
26728
|
-
playsinline: playsInline,
|
|
26729
|
-
preload: "metadata",
|
|
26730
|
-
autoplay: autoplay ? "any" : false,
|
|
26731
|
-
muted,
|
|
26732
|
-
loop,
|
|
26733
|
-
poster,
|
|
26734
|
-
// Optimized HLS configuration for VOD content with LARGE SEGMENTS (40MB)
|
|
26735
|
-
// Strategy: Aggressive buffer reduction for faster startup with large segments
|
|
26736
|
-
html5: {
|
|
26737
|
-
vhs: {
|
|
26738
|
-
// VHS (Video HTTP Streaming) options for HLS
|
|
26739
|
-
withCredentials: false,
|
|
26740
|
-
handleManifestRedirect: true,
|
|
26741
|
-
overrideNative: !videojs__default.default.browser.IS_SAFARI,
|
|
26742
|
-
// Use native HLS on Safari
|
|
26743
|
-
enableLowInitialPlaylist: true,
|
|
26744
|
-
smoothQualityChange: true,
|
|
26745
|
-
// Optimized bandwidth and buffering for VOD
|
|
26746
|
-
bandwidth: 5e7,
|
|
26747
|
-
// Start with high bandwidth assumption (50 Mbps)
|
|
26748
|
-
initialBandwidth: 5e7,
|
|
26749
|
-
// Assume good connection initially
|
|
26750
|
-
limitRenditionByPlayerDimensions: true,
|
|
26751
|
-
// Buffer configuration optimized for large segments (40MB each)
|
|
26752
|
-
maxBufferLength: 3,
|
|
26753
|
-
// Start playing with just 3 seconds ahead
|
|
26754
|
-
maxMaxBufferLength: 8,
|
|
26755
|
-
// Maximum 8 seconds buffer for faster startup
|
|
26756
|
-
maxBufferSize: 50 * 1e3 * 1e3,
|
|
26757
|
-
// 50MB max buffer size (1.25 segments)
|
|
26758
|
-
maxBufferHole: 0.25,
|
|
26759
|
-
// Smaller holes for better continuity
|
|
26760
|
-
bufferBasedABR: false,
|
|
26761
|
-
// Disable for more predictable behavior
|
|
26762
|
-
// Segment loading optimization for large segments (40MB)
|
|
26763
|
-
maxPlaylistRetries: 3,
|
|
26764
|
-
playlistRetryDelay: 500,
|
|
26765
|
-
// 500ms between retries
|
|
26766
|
-
playlistExclusionDuration: 60,
|
|
26767
|
-
segmentLoadingRetryAttempts: 5,
|
|
26768
|
-
// More retries for large segments
|
|
26769
|
-
segmentLoadingRetryDelay: 500,
|
|
26770
|
-
// Faster retry for responsiveness
|
|
26771
|
-
segmentLoadingTimeOut: 6e4,
|
|
26772
|
-
// 60s timeout for 40MB segments
|
|
26773
|
-
manifestLoadingTimeOut: 15e3,
|
|
26774
|
-
// 15s timeout for manifest
|
|
26775
|
-
// Performance optimizations
|
|
26776
|
-
experimentalBufferBasedCodecSwitching: true,
|
|
26777
|
-
experimentalCacheEncryptionKeys: true,
|
|
26778
|
-
handlePartialData: true,
|
|
26779
|
-
allowSeeksWithinUnsafeLiveWindow: false,
|
|
26780
|
-
// VOD content
|
|
26781
|
-
experimentalLLHLS: false,
|
|
26782
|
-
// Disable Low Latency HLS for VOD
|
|
26783
|
-
// Connection settings
|
|
26784
|
-
// Chrome 130+ started throwing "Cannot perform Construct on a detached ArrayBuffer"
|
|
26785
|
-
// whenever the transmux worker tried to rehydrate transferred buffers that originated
|
|
26786
|
-
// from Blob-based playlists. Disabling the worker keeps playback stable at the cost
|
|
26787
|
-
// of slightly higher main-thread usage, which is acceptable for the dashboard usage.
|
|
26788
|
-
enableWorker: false,
|
|
26789
|
-
progressive: true,
|
|
26790
|
-
// Progressive download
|
|
26791
|
-
// Adaptive bitrate settings (if multi-quality available)
|
|
26792
|
-
abrEwmaFastLive: 3,
|
|
26793
|
-
abrEwmaSlowLive: 9,
|
|
26794
|
-
abrBandWidthFactor: 0.95,
|
|
26795
|
-
abrBandWidthUpFactor: 0.7,
|
|
26796
|
-
abrMaxWithRealBitrate: false,
|
|
26797
|
-
// Request options optimized for large segments
|
|
26798
|
-
requestOptions: {
|
|
26799
|
-
timeout: 9e4,
|
|
26800
|
-
// 90s timeout for 40MB segments
|
|
26801
|
-
maxRetry: 5
|
|
26802
|
-
// More retries for reliability
|
|
26803
|
-
}
|
|
26804
|
-
},
|
|
26805
|
-
nativeVideoTracks: false,
|
|
26806
|
-
nativeAudioTracks: false,
|
|
26807
|
-
nativeTextTracks: false
|
|
26808
|
-
},
|
|
26809
|
-
// Improved seeking and scrubbing
|
|
26810
|
-
inactivityTimeout: 3e3,
|
|
26811
|
-
// Better error handling
|
|
26812
|
-
errorDisplay: false,
|
|
26813
|
-
// We'll handle errors with callbacks
|
|
26814
|
-
// Fullscreen options
|
|
26815
|
-
fullscreen: {
|
|
26816
|
-
options: {
|
|
26817
|
-
navigationUI: "hide"
|
|
26818
|
-
}
|
|
26819
|
-
},
|
|
26820
|
-
...options
|
|
26821
|
-
};
|
|
26822
|
-
const initializePlayer = React23.useCallback(() => {
|
|
26823
|
-
if (!videoRef.current || playerRef.current) return;
|
|
26824
|
-
const videoElement = document.createElement("video-js");
|
|
26825
|
-
videoElement.className = "vjs-default-skin";
|
|
26826
|
-
videoRef.current.appendChild(videoElement);
|
|
26827
|
-
const player = videojs__default.default(videoElement, defaultOptions);
|
|
26828
|
-
playerRef.current = player;
|
|
26829
|
-
player.ready(() => {
|
|
26830
|
-
setIsReady(true);
|
|
26831
|
-
onReady?.(player);
|
|
26832
|
-
});
|
|
26833
|
-
player.on("play", () => onPlay?.(player));
|
|
26834
|
-
player.on("pause", () => onPause?.(player));
|
|
26835
|
-
player.on("playing", () => onPlaying?.(player));
|
|
26836
|
-
player.on("timeupdate", () => {
|
|
26837
|
-
const currentTime2 = player.currentTime() || 0;
|
|
26838
|
-
onTimeUpdate?.(player, currentTime2);
|
|
26839
|
-
});
|
|
26840
|
-
player.on("durationchange", () => {
|
|
26841
|
-
const duration2 = player.duration() || 0;
|
|
26842
|
-
onDurationChange?.(player, duration2);
|
|
26843
|
-
});
|
|
26844
|
-
player.on("ended", () => onEnded?.(player));
|
|
26845
|
-
player.on("loadstart", () => {
|
|
26569
|
+
const handleDurationChange = () => {
|
|
26570
|
+
const duration2 = video.duration || 0;
|
|
26571
|
+
eventCallbacksRef.current.onDurationChange?.(player, duration2);
|
|
26572
|
+
};
|
|
26573
|
+
const handleEnded = () => eventCallbacksRef.current.onEnded?.(player);
|
|
26574
|
+
const handleLoadStart = () => {
|
|
26846
26575
|
setIsLoading(true);
|
|
26847
|
-
onLoadingChange?.(true);
|
|
26848
|
-
onLoadStart?.(player);
|
|
26849
|
-
}
|
|
26850
|
-
|
|
26576
|
+
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
26577
|
+
eventCallbacksRef.current.onLoadStart?.(player);
|
|
26578
|
+
};
|
|
26579
|
+
const handleLoadedMetadata = () => eventCallbacksRef.current.onLoadedMetadata?.(player);
|
|
26580
|
+
const handleLoadedData = () => {
|
|
26851
26581
|
setIsLoading(false);
|
|
26852
|
-
onLoadingChange?.(false);
|
|
26853
|
-
onLoadedData?.(player);
|
|
26854
|
-
}
|
|
26855
|
-
|
|
26582
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26583
|
+
eventCallbacksRef.current.onLoadedData?.(player);
|
|
26584
|
+
};
|
|
26585
|
+
const handleWaiting = () => {
|
|
26856
26586
|
setIsLoading(true);
|
|
26857
|
-
onLoadingChange?.(true);
|
|
26858
|
-
}
|
|
26859
|
-
|
|
26860
|
-
|
|
26861
|
-
|
|
26862
|
-
|
|
26863
|
-
|
|
26864
|
-
|
|
26865
|
-
|
|
26866
|
-
|
|
26867
|
-
|
|
26868
|
-
|
|
26869
|
-
|
|
26870
|
-
|
|
26871
|
-
const errorInfo = ERROR_MAPPING[errorCode] || {
|
|
26872
|
-
code: errorCode || 0,
|
|
26873
|
-
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
26874
|
-
message: "Unknown playback error occurred",
|
|
26875
|
-
canRetry: false
|
|
26876
|
-
};
|
|
26877
|
-
console.error("[VideoPlayer] Video.js error:", {
|
|
26878
|
-
code: errorCode,
|
|
26879
|
-
type: errorInfo.type,
|
|
26880
|
-
message: errorInfo.message,
|
|
26881
|
-
canRetry: errorInfo.canRetry,
|
|
26882
|
-
originalError: error
|
|
26883
|
-
});
|
|
26884
|
-
onError?.(player, errorInfo);
|
|
26885
|
-
});
|
|
26886
|
-
if (src) {
|
|
26887
|
-
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
26888
|
-
let videoSrc = src;
|
|
26889
|
-
let blobUrl = null;
|
|
26890
|
-
if (src.startsWith("#EXTM3U")) {
|
|
26891
|
-
const safariMode = isSafari();
|
|
26892
|
-
const browserName = getBrowserName();
|
|
26893
|
-
if (safariMode) {
|
|
26894
|
-
const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
|
|
26895
|
-
if (clipIdMatch) {
|
|
26896
|
-
videoSrc = `/api/clips/playlist/${clipIdMatch[1]}`;
|
|
26897
|
-
console.log(`[VideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, videoSrc);
|
|
26898
|
-
} else {
|
|
26899
|
-
console.warn("[VideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
|
|
26900
|
-
const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
|
|
26901
|
-
blobUrl = URL.createObjectURL(blob);
|
|
26902
|
-
videoSrc = blobUrl;
|
|
26903
|
-
player._blobUrl = videoSrc;
|
|
26904
|
-
}
|
|
26905
|
-
} else {
|
|
26906
|
-
const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
|
|
26907
|
-
blobUrl = URL.createObjectURL(blob);
|
|
26908
|
-
videoSrc = blobUrl;
|
|
26909
|
-
console.log(`[VideoPlayer] Non-Safari browser (${browserName}) - using blob URL for optimal performance`);
|
|
26910
|
-
player._blobUrl = videoSrc;
|
|
26911
|
-
}
|
|
26587
|
+
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
26588
|
+
};
|
|
26589
|
+
const handleSeeking = () => eventCallbacksRef.current.onSeeking?.(player);
|
|
26590
|
+
const handleSeeked = () => eventCallbacksRef.current.onSeeked?.(player);
|
|
26591
|
+
const handleError = () => {
|
|
26592
|
+
const error = video.error;
|
|
26593
|
+
if (error) {
|
|
26594
|
+
const errorInfo = {
|
|
26595
|
+
code: error.code,
|
|
26596
|
+
type: error.code <= 2 ? "recoverable" : "non_recoverable",
|
|
26597
|
+
message: error.message || "Unknown error occurred",
|
|
26598
|
+
canRetry: error.code <= 2
|
|
26599
|
+
};
|
|
26600
|
+
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
26912
26601
|
}
|
|
26913
|
-
|
|
26914
|
-
|
|
26915
|
-
|
|
26916
|
-
|
|
26917
|
-
|
|
26602
|
+
};
|
|
26603
|
+
video.addEventListener("canplay", handleCanPlay);
|
|
26604
|
+
video.addEventListener("play", handlePlay);
|
|
26605
|
+
video.addEventListener("pause", handlePause);
|
|
26606
|
+
video.addEventListener("playing", handlePlaying);
|
|
26607
|
+
video.addEventListener("timeupdate", handleTimeUpdate);
|
|
26608
|
+
video.addEventListener("durationchange", handleDurationChange);
|
|
26609
|
+
video.addEventListener("ended", handleEnded);
|
|
26610
|
+
video.addEventListener("loadstart", handleLoadStart);
|
|
26611
|
+
video.addEventListener("loadedmetadata", handleLoadedMetadata);
|
|
26612
|
+
video.addEventListener("loadeddata", handleLoadedData);
|
|
26613
|
+
video.addEventListener("waiting", handleWaiting);
|
|
26614
|
+
video.addEventListener("seeking", handleSeeking);
|
|
26615
|
+
video.addEventListener("seeked", handleSeeked);
|
|
26616
|
+
video.addEventListener("error", handleError);
|
|
26617
|
+
return () => {
|
|
26618
|
+
video.removeEventListener("canplay", handleCanPlay);
|
|
26619
|
+
video.removeEventListener("play", handlePlay);
|
|
26620
|
+
video.removeEventListener("pause", handlePause);
|
|
26621
|
+
video.removeEventListener("playing", handlePlaying);
|
|
26622
|
+
video.removeEventListener("timeupdate", handleTimeUpdate);
|
|
26623
|
+
video.removeEventListener("durationchange", handleDurationChange);
|
|
26624
|
+
video.removeEventListener("ended", handleEnded);
|
|
26625
|
+
video.removeEventListener("loadstart", handleLoadStart);
|
|
26626
|
+
video.removeEventListener("loadedmetadata", handleLoadedMetadata);
|
|
26627
|
+
video.removeEventListener("loadeddata", handleLoadedData);
|
|
26628
|
+
video.removeEventListener("waiting", handleWaiting);
|
|
26629
|
+
video.removeEventListener("seeking", handleSeeking);
|
|
26630
|
+
video.removeEventListener("seeked", handleSeeked);
|
|
26631
|
+
video.removeEventListener("error", handleError);
|
|
26632
|
+
};
|
|
26918
26633
|
}, [
|
|
26919
26634
|
src,
|
|
26920
|
-
|
|
26921
|
-
|
|
26922
|
-
|
|
26923
|
-
onPause,
|
|
26924
|
-
onTimeUpdate,
|
|
26925
|
-
onDurationChange,
|
|
26926
|
-
onEnded,
|
|
26927
|
-
onError,
|
|
26928
|
-
onLoadStart,
|
|
26929
|
-
onLoadedMetadata,
|
|
26930
|
-
onSeeking,
|
|
26931
|
-
onSeeked
|
|
26635
|
+
cleanupBlobUrl,
|
|
26636
|
+
playerLikeObject,
|
|
26637
|
+
configVersion
|
|
26932
26638
|
]);
|
|
26933
26639
|
React23.useEffect(() => {
|
|
26934
|
-
|
|
26935
|
-
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
26936
|
-
let videoSrc = src;
|
|
26937
|
-
let blobUrl = null;
|
|
26938
|
-
if (src.startsWith("#EXTM3U")) {
|
|
26939
|
-
const safariMode = isSafari();
|
|
26940
|
-
const browserName = getBrowserName();
|
|
26941
|
-
if (safariMode) {
|
|
26942
|
-
const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
|
|
26943
|
-
if (clipIdMatch) {
|
|
26944
|
-
videoSrc = `/api/clips/playlist/${clipIdMatch[1]}`;
|
|
26945
|
-
console.log(`[VideoPlayer] Safari detected (${browserName}) - using playlist proxy URL for source update:`, videoSrc);
|
|
26946
|
-
} else {
|
|
26947
|
-
console.warn("[VideoPlayer] Safari detected but no clip ID found in playlist (source update), trying blob URL fallback");
|
|
26948
|
-
const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
|
|
26949
|
-
blobUrl = URL.createObjectURL(blob);
|
|
26950
|
-
videoSrc = blobUrl;
|
|
26951
|
-
}
|
|
26952
|
-
} else {
|
|
26953
|
-
const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
|
|
26954
|
-
blobUrl = URL.createObjectURL(blob);
|
|
26955
|
-
videoSrc = blobUrl;
|
|
26956
|
-
console.log(`[VideoPlayer] Non-Safari browser (${browserName}) - using blob URL for source update`);
|
|
26957
|
-
}
|
|
26958
|
-
}
|
|
26959
|
-
playerRef.current.src({
|
|
26960
|
-
src: videoSrc,
|
|
26961
|
-
type: isHLS ? "application/x-mpegURL" : "video/mp4"
|
|
26962
|
-
});
|
|
26963
|
-
return () => {
|
|
26964
|
-
if (blobUrl) {
|
|
26965
|
-
URL.revokeObjectURL(blobUrl);
|
|
26966
|
-
}
|
|
26967
|
-
};
|
|
26968
|
-
}
|
|
26969
|
-
}, [src]);
|
|
26970
|
-
React23.useEffect(() => {
|
|
26971
|
-
initializePlayer();
|
|
26640
|
+
const cleanup = initializePlayer();
|
|
26972
26641
|
return () => {
|
|
26973
|
-
|
|
26974
|
-
|
|
26975
|
-
|
|
26976
|
-
|
|
26977
|
-
}
|
|
26978
|
-
playerRef.current.dispose();
|
|
26979
|
-
playerRef.current = null;
|
|
26980
|
-
setIsReady(false);
|
|
26642
|
+
cleanup?.();
|
|
26643
|
+
if (hlsRef.current) {
|
|
26644
|
+
hlsRef.current.destroy();
|
|
26645
|
+
hlsRef.current = null;
|
|
26981
26646
|
}
|
|
26647
|
+
cleanupBlobUrl();
|
|
26648
|
+
setIsReady(false);
|
|
26982
26649
|
};
|
|
26983
|
-
}, []);
|
|
26650
|
+
}, [src, initializePlayer, cleanupBlobUrl]);
|
|
26651
|
+
React23.useEffect(() => {
|
|
26652
|
+
if (videoRef.current) {
|
|
26653
|
+
if (autoplay) {
|
|
26654
|
+
videoRef.current.play().catch((err) => {
|
|
26655
|
+
console.warn("[HlsVideoPlayer] Autoplay failed:", err);
|
|
26656
|
+
});
|
|
26657
|
+
}
|
|
26658
|
+
}
|
|
26659
|
+
}, [autoplay]);
|
|
26984
26660
|
const play = React23.useCallback(() => {
|
|
26985
|
-
return
|
|
26661
|
+
return videoRef.current?.play();
|
|
26986
26662
|
}, []);
|
|
26987
26663
|
const pause = React23.useCallback(() => {
|
|
26988
|
-
|
|
26664
|
+
videoRef.current?.pause();
|
|
26989
26665
|
}, []);
|
|
26990
26666
|
const currentTime = React23.useCallback((time2) => {
|
|
26991
|
-
if (time2 !== void 0) {
|
|
26992
|
-
|
|
26667
|
+
if (time2 !== void 0 && videoRef.current) {
|
|
26668
|
+
videoRef.current.currentTime = time2;
|
|
26993
26669
|
return time2;
|
|
26994
26670
|
}
|
|
26995
|
-
return
|
|
26671
|
+
return videoRef.current?.currentTime || 0;
|
|
26996
26672
|
}, []);
|
|
26997
26673
|
const duration = React23.useCallback(() => {
|
|
26998
|
-
return
|
|
26674
|
+
return videoRef.current?.duration || 0;
|
|
26999
26675
|
}, []);
|
|
27000
26676
|
const paused = React23.useCallback(() => {
|
|
27001
|
-
return
|
|
26677
|
+
return videoRef.current?.paused ?? true;
|
|
27002
26678
|
}, []);
|
|
27003
26679
|
const mute = React23.useCallback((isMuted) => {
|
|
27004
|
-
if (isMuted !== void 0) {
|
|
27005
|
-
|
|
26680
|
+
if (isMuted !== void 0 && videoRef.current) {
|
|
26681
|
+
videoRef.current.muted = isMuted;
|
|
27006
26682
|
return isMuted;
|
|
27007
26683
|
}
|
|
27008
|
-
return
|
|
26684
|
+
return videoRef.current?.muted ?? false;
|
|
27009
26685
|
}, []);
|
|
27010
26686
|
const volume = React23.useCallback((level) => {
|
|
27011
|
-
if (level !== void 0) {
|
|
27012
|
-
|
|
26687
|
+
if (level !== void 0 && videoRef.current) {
|
|
26688
|
+
videoRef.current.volume = level;
|
|
27013
26689
|
return level;
|
|
27014
26690
|
}
|
|
27015
|
-
return
|
|
26691
|
+
return videoRef.current?.volume ?? 1;
|
|
27016
26692
|
}, []);
|
|
27017
|
-
const
|
|
27018
|
-
if (
|
|
27019
|
-
|
|
27020
|
-
|
|
27021
|
-
setIsReady(false);
|
|
26693
|
+
const playbackRate = React23.useCallback((rate) => {
|
|
26694
|
+
if (rate !== void 0 && videoRef.current) {
|
|
26695
|
+
videoRef.current.playbackRate = rate;
|
|
26696
|
+
return rate;
|
|
27022
26697
|
}
|
|
26698
|
+
return videoRef.current?.playbackRate ?? 1;
|
|
27023
26699
|
}, []);
|
|
27024
|
-
|
|
27025
|
-
|
|
26700
|
+
React23.useImperativeHandle(ref, () => ({
|
|
26701
|
+
hls: hlsRef.current,
|
|
26702
|
+
video: videoRef.current,
|
|
27026
26703
|
play,
|
|
27027
26704
|
pause,
|
|
27028
26705
|
currentTime,
|
|
@@ -27030,13 +26707,15 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
27030
26707
|
paused,
|
|
27031
26708
|
mute,
|
|
27032
26709
|
volume,
|
|
26710
|
+
playbackRate,
|
|
27033
26711
|
dispose,
|
|
27034
|
-
isReady
|
|
27035
|
-
|
|
26712
|
+
isReady,
|
|
26713
|
+
// For backward compatibility with Video.js API
|
|
26714
|
+
player: playerLikeObject()
|
|
26715
|
+
}), [play, pause, currentTime, duration, paused, mute, volume, playbackRate, dispose, isReady, playerLikeObject]);
|
|
27036
26716
|
const handleClickWithIndicator = React23.useCallback(() => {
|
|
27037
|
-
if (!onClick || !
|
|
27038
|
-
const
|
|
27039
|
-
const willBePlaying = player.paused();
|
|
26717
|
+
if (!onClick || !videoRef.current) return;
|
|
26718
|
+
const willBePlaying = videoRef.current.paused;
|
|
27040
26719
|
setIndicatorIsPlaying(willBePlaying);
|
|
27041
26720
|
setShowIndicator(false);
|
|
27042
26721
|
setTimeout(() => {
|
|
@@ -27045,17 +26724,30 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
27045
26724
|
}, 0);
|
|
27046
26725
|
onClick();
|
|
27047
26726
|
}, [onClick]);
|
|
27048
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `video-player-wrapper ${className}`, style: { position: "relative", width: "100%", height: "100%" }, children: [
|
|
26727
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `hls-video-player-wrapper ${className}`, style: { position: "relative", width: "100%", height: "100%" }, children: [
|
|
27049
26728
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27050
26729
|
"div",
|
|
27051
26730
|
{
|
|
27052
|
-
className: "video-player-container",
|
|
27053
|
-
ref:
|
|
27054
|
-
|
|
26731
|
+
className: "hls-video-player-container",
|
|
26732
|
+
ref: videoContainerRef,
|
|
26733
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
26734
|
+
"video",
|
|
26735
|
+
{
|
|
26736
|
+
ref: videoRef,
|
|
26737
|
+
className: "hls-video-element",
|
|
26738
|
+
poster,
|
|
26739
|
+
controls,
|
|
26740
|
+
loop,
|
|
26741
|
+
muted,
|
|
26742
|
+
playsInline,
|
|
26743
|
+
autoPlay: autoplay,
|
|
26744
|
+
preload: "metadata"
|
|
26745
|
+
}
|
|
26746
|
+
)
|
|
27055
26747
|
}
|
|
27056
26748
|
),
|
|
27057
|
-
isLoading && !externalLoadingControl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "video-player-loading", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
27058
|
-
onClick && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26749
|
+
isLoading && !externalLoadingControl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hls-video-player-loading", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
26750
|
+
onClick && !controls && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27059
26751
|
"div",
|
|
27060
26752
|
{
|
|
27061
26753
|
onClick: handleClickWithIndicator,
|
|
@@ -27071,7 +26763,7 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
27071
26763
|
"aria-label": "Click to play/pause"
|
|
27072
26764
|
}
|
|
27073
26765
|
),
|
|
27074
|
-
onClick && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26766
|
+
onClick && !controls && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27075
26767
|
PlayPauseIndicator,
|
|
27076
26768
|
{
|
|
27077
26769
|
show: showIndicator,
|
|
@@ -27081,13 +26773,25 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
27081
26773
|
)
|
|
27082
26774
|
] });
|
|
27083
26775
|
});
|
|
27084
|
-
|
|
27085
|
-
var
|
|
26776
|
+
HlsVideoPlayer.displayName = "HlsVideoPlayer";
|
|
26777
|
+
var VideoPlayer = HlsVideoPlayer;
|
|
26778
|
+
var CroppedHlsVideoPlayer = React23.forwardRef(({
|
|
27086
26779
|
crop,
|
|
27087
26780
|
debug = false,
|
|
27088
26781
|
onClick,
|
|
27089
26782
|
...videoProps
|
|
27090
26783
|
}, ref) => {
|
|
26784
|
+
const {
|
|
26785
|
+
onReady: onReadyProp,
|
|
26786
|
+
onPlay: onPlayProp,
|
|
26787
|
+
onPause: onPauseProp,
|
|
26788
|
+
onEnded: onEndedProp,
|
|
26789
|
+
onSeeking: onSeekingProp,
|
|
26790
|
+
onSeeked: onSeekedProp,
|
|
26791
|
+
onLoadedMetadata: onLoadedMetadataProp,
|
|
26792
|
+
className: inheritedClassName = ""
|
|
26793
|
+
} = videoProps;
|
|
26794
|
+
const videoSrc = videoProps.src;
|
|
27091
26795
|
const videoContainerRef = React23.useRef(null);
|
|
27092
26796
|
const hiddenVideoRef = React23.useRef(null);
|
|
27093
26797
|
const canvasRef = React23.useRef(null);
|
|
@@ -27106,8 +26810,11 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27106
26810
|
}
|
|
27107
26811
|
}, []);
|
|
27108
26812
|
React23.useImperativeHandle(ref, () => ({
|
|
27109
|
-
get
|
|
27110
|
-
return hiddenVideoRef.current?.
|
|
26813
|
+
get hls() {
|
|
26814
|
+
return hiddenVideoRef.current?.hls || null;
|
|
26815
|
+
},
|
|
26816
|
+
get video() {
|
|
26817
|
+
return hiddenVideoRef.current?.video || null;
|
|
27111
26818
|
},
|
|
27112
26819
|
play: () => hiddenVideoRef.current?.play() || void 0,
|
|
27113
26820
|
pause: () => hiddenVideoRef.current?.pause(),
|
|
@@ -27121,12 +26828,36 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27121
26828
|
paused: () => hiddenVideoRef.current?.paused() || true,
|
|
27122
26829
|
mute: (isMuted) => hiddenVideoRef.current?.mute(isMuted) || false,
|
|
27123
26830
|
volume: (level) => hiddenVideoRef.current?.volume(level) || 0,
|
|
26831
|
+
playbackRate: (rate) => hiddenVideoRef.current?.playbackRate(rate) || 1,
|
|
27124
26832
|
dispose: () => {
|
|
27125
26833
|
hiddenVideoRef.current?.dispose();
|
|
27126
26834
|
stopCanvasRendering();
|
|
27127
26835
|
},
|
|
27128
26836
|
get isReady() {
|
|
27129
26837
|
return hiddenVideoRef.current?.isReady || false;
|
|
26838
|
+
},
|
|
26839
|
+
// For backward compatibility with Video.js API
|
|
26840
|
+
get player() {
|
|
26841
|
+
const video = hiddenVideoRef.current?.video;
|
|
26842
|
+
if (!video) return null;
|
|
26843
|
+
return {
|
|
26844
|
+
el: () => video,
|
|
26845
|
+
currentTime: () => video.currentTime || 0,
|
|
26846
|
+
duration: () => video.duration || 0,
|
|
26847
|
+
paused: () => video.paused ?? true,
|
|
26848
|
+
play: () => video.play(),
|
|
26849
|
+
pause: () => video.pause(),
|
|
26850
|
+
muted: (val) => {
|
|
26851
|
+
if (val !== void 0) video.muted = val;
|
|
26852
|
+
return video.muted;
|
|
26853
|
+
},
|
|
26854
|
+
volume: (val) => {
|
|
26855
|
+
if (val !== void 0) video.volume = val;
|
|
26856
|
+
return video.volume;
|
|
26857
|
+
},
|
|
26858
|
+
error: () => null,
|
|
26859
|
+
dispose: () => hiddenVideoRef.current?.dispose()
|
|
26860
|
+
};
|
|
27130
26861
|
}
|
|
27131
26862
|
}), [stopCanvasRendering]);
|
|
27132
26863
|
const calculateCanvasDimensions = React23.useCallback(() => {
|
|
@@ -27186,24 +26917,38 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27186
26917
|
animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
|
|
27187
26918
|
}, [crop]);
|
|
27188
26919
|
const handleVideoReady = React23.useCallback((player) => {
|
|
27189
|
-
console.log("[
|
|
27190
|
-
const videoEl =
|
|
26920
|
+
console.log("[CroppedHlsVideoPlayer] Video player ready");
|
|
26921
|
+
const videoEl = hiddenVideoRef.current?.video;
|
|
27191
26922
|
if (videoEl) {
|
|
27192
26923
|
videoElementRef.current = videoEl;
|
|
27193
26924
|
setIsVideoReady(true);
|
|
27194
26925
|
}
|
|
27195
|
-
|
|
27196
|
-
}, [
|
|
26926
|
+
onReadyProp?.(player);
|
|
26927
|
+
}, [onReadyProp]);
|
|
27197
26928
|
const handleVideoPlay = React23.useCallback((player) => {
|
|
27198
|
-
console.log("[
|
|
26929
|
+
console.log("[CroppedHlsVideoPlayer] Video playing, starting canvas rendering");
|
|
27199
26930
|
if (crop && canvasRef.current) {
|
|
27200
26931
|
setIsProcessing(true);
|
|
27201
26932
|
renderFrameToCanvas();
|
|
27202
26933
|
}
|
|
27203
|
-
|
|
27204
|
-
}, [crop, renderFrameToCanvas,
|
|
26934
|
+
onPlayProp?.(player);
|
|
26935
|
+
}, [crop, renderFrameToCanvas, onPlayProp]);
|
|
27205
26936
|
const handleVideoPause = React23.useCallback((player) => {
|
|
27206
|
-
console.log("[
|
|
26937
|
+
console.log("[CroppedHlsVideoPlayer] Video paused, stopping canvas rendering and CLEARING canvas");
|
|
26938
|
+
stopCanvasRendering();
|
|
26939
|
+
setIsProcessing(false);
|
|
26940
|
+
if (canvasRef.current) {
|
|
26941
|
+
const ctx = canvasRef.current.getContext("2d");
|
|
26942
|
+
if (ctx) {
|
|
26943
|
+
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
26944
|
+
ctx.fillStyle = "black";
|
|
26945
|
+
ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
26946
|
+
}
|
|
26947
|
+
}
|
|
26948
|
+
onPauseProp?.(player);
|
|
26949
|
+
}, [stopCanvasRendering, onPauseProp]);
|
|
26950
|
+
const handleVideoEnded = React23.useCallback((player) => {
|
|
26951
|
+
console.log("[CroppedHlsVideoPlayer] Video ended, CLEARING canvas");
|
|
27207
26952
|
stopCanvasRendering();
|
|
27208
26953
|
setIsProcessing(false);
|
|
27209
26954
|
if (canvasRef.current) {
|
|
@@ -27214,159 +26959,677 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27214
26959
|
ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
27215
26960
|
}
|
|
27216
26961
|
}
|
|
27217
|
-
|
|
27218
|
-
}, [stopCanvasRendering,
|
|
27219
|
-
const
|
|
27220
|
-
console.log("[
|
|
27221
|
-
|
|
27222
|
-
|
|
27223
|
-
|
|
27224
|
-
|
|
27225
|
-
|
|
27226
|
-
|
|
27227
|
-
|
|
27228
|
-
|
|
26962
|
+
onEndedProp?.(player);
|
|
26963
|
+
}, [stopCanvasRendering, onEndedProp]);
|
|
26964
|
+
const handleSeeking = React23.useCallback((player) => {
|
|
26965
|
+
console.log("[CroppedHlsVideoPlayer] Video seeking");
|
|
26966
|
+
if (crop && !videoElementRef.current?.paused) {
|
|
26967
|
+
renderFrameToCanvas();
|
|
26968
|
+
}
|
|
26969
|
+
onSeekingProp?.(player);
|
|
26970
|
+
}, [crop, renderFrameToCanvas, onSeekingProp]);
|
|
26971
|
+
const handleSeeked = React23.useCallback((player) => {
|
|
26972
|
+
console.log("[CroppedHlsVideoPlayer] Video seeked");
|
|
26973
|
+
if (crop && !videoElementRef.current?.paused) {
|
|
26974
|
+
renderFrameToCanvas();
|
|
26975
|
+
}
|
|
26976
|
+
onSeekedProp?.(player);
|
|
26977
|
+
}, [crop, renderFrameToCanvas, onSeekedProp]);
|
|
26978
|
+
const handleLoadedMetadata = React23.useCallback((player) => {
|
|
26979
|
+
console.log("[CroppedHlsVideoPlayer] Video metadata loaded");
|
|
26980
|
+
calculateCanvasDimensions();
|
|
26981
|
+
onLoadedMetadataProp?.(player);
|
|
26982
|
+
}, [calculateCanvasDimensions, onLoadedMetadataProp]);
|
|
26983
|
+
React23.useEffect(() => {
|
|
26984
|
+
calculateCanvasDimensions();
|
|
26985
|
+
const handleResize = () => {
|
|
26986
|
+
calculateCanvasDimensions();
|
|
26987
|
+
};
|
|
26988
|
+
window.addEventListener("resize", handleResize);
|
|
26989
|
+
return () => {
|
|
26990
|
+
window.removeEventListener("resize", handleResize);
|
|
26991
|
+
};
|
|
26992
|
+
}, [calculateCanvasDimensions]);
|
|
26993
|
+
React23.useLayoutEffect(() => {
|
|
26994
|
+
if (canvasRef.current && crop) {
|
|
26995
|
+
const canvas = canvasRef.current;
|
|
26996
|
+
const ctx = canvas.getContext("2d");
|
|
26997
|
+
if (ctx) {
|
|
26998
|
+
console.log("[CroppedHlsVideoPlayer] Source changing - CLEARING CANVAS IMMEDIATELY");
|
|
26999
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
27000
|
+
ctx.fillStyle = "black";
|
|
27001
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
27002
|
+
}
|
|
27003
|
+
}
|
|
27004
|
+
}, [videoSrc, crop]);
|
|
27005
|
+
React23.useEffect(() => {
|
|
27006
|
+
return () => {
|
|
27007
|
+
stopCanvasRendering();
|
|
27008
|
+
};
|
|
27009
|
+
}, [stopCanvasRendering]);
|
|
27010
|
+
if (!crop) {
|
|
27011
|
+
return /* @__PURE__ */ jsxRuntime.jsx(HlsVideoPlayer, { ref, ...videoProps, onClick });
|
|
27012
|
+
}
|
|
27013
|
+
const handleClickWithIndicator = () => {
|
|
27014
|
+
if (!onClick || !hiddenVideoRef.current?.video) return;
|
|
27015
|
+
const video = hiddenVideoRef.current.video;
|
|
27016
|
+
const willBePlaying = video.paused;
|
|
27017
|
+
setIndicatorIsPlaying(willBePlaying);
|
|
27018
|
+
setShowIndicator(false);
|
|
27019
|
+
setTimeout(() => {
|
|
27020
|
+
indicatorKeyRef.current += 1;
|
|
27021
|
+
setShowIndicator(true);
|
|
27022
|
+
}, 0);
|
|
27023
|
+
onClick();
|
|
27024
|
+
};
|
|
27025
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27026
|
+
"div",
|
|
27027
|
+
{
|
|
27028
|
+
ref: videoContainerRef,
|
|
27029
|
+
className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""} ${inheritedClassName}`,
|
|
27030
|
+
onClick: handleClickWithIndicator,
|
|
27031
|
+
children: [
|
|
27032
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
27033
|
+
HlsVideoPlayer,
|
|
27034
|
+
{
|
|
27035
|
+
ref: hiddenVideoRef,
|
|
27036
|
+
...videoProps,
|
|
27037
|
+
onReady: handleVideoReady,
|
|
27038
|
+
onPlay: handleVideoPlay,
|
|
27039
|
+
onPause: handleVideoPause,
|
|
27040
|
+
onEnded: handleVideoEnded,
|
|
27041
|
+
onSeeking: handleSeeking,
|
|
27042
|
+
onSeeked: handleSeeked,
|
|
27043
|
+
onLoadedMetadata: handleLoadedMetadata,
|
|
27044
|
+
onLoadedData: videoProps.onLoadedData,
|
|
27045
|
+
onPlaying: videoProps.onPlaying,
|
|
27046
|
+
onLoadingChange: videoProps.onLoadingChange
|
|
27047
|
+
}
|
|
27048
|
+
) }),
|
|
27049
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27050
|
+
"canvas",
|
|
27051
|
+
{
|
|
27052
|
+
ref: canvasRef,
|
|
27053
|
+
width: canvasDimensions.width,
|
|
27054
|
+
height: canvasDimensions.height,
|
|
27055
|
+
className: "max-w-full max-h-full",
|
|
27056
|
+
style: {
|
|
27057
|
+
display: isVideoReady ? "block" : "none",
|
|
27058
|
+
width: `${canvasDimensions.width}px`,
|
|
27059
|
+
height: `${canvasDimensions.height}px`
|
|
27060
|
+
}
|
|
27061
|
+
}
|
|
27062
|
+
),
|
|
27063
|
+
!isVideoReady && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
27064
|
+
debug && isVideoReady && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
|
|
27065
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
27066
|
+
"Crop: ",
|
|
27067
|
+
crop.x,
|
|
27068
|
+
",",
|
|
27069
|
+
crop.y,
|
|
27070
|
+
" ",
|
|
27071
|
+
crop.width,
|
|
27072
|
+
"x",
|
|
27073
|
+
crop.height,
|
|
27074
|
+
"%"
|
|
27075
|
+
] }),
|
|
27076
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
27077
|
+
"Canvas: ",
|
|
27078
|
+
canvasDimensions.width,
|
|
27079
|
+
"x",
|
|
27080
|
+
canvasDimensions.height,
|
|
27081
|
+
"px"
|
|
27082
|
+
] }),
|
|
27083
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
27084
|
+
"Processing: ",
|
|
27085
|
+
isProcessing ? "Yes" : "No"
|
|
27086
|
+
] })
|
|
27087
|
+
] }),
|
|
27088
|
+
onClick && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27089
|
+
PlayPauseIndicator,
|
|
27090
|
+
{
|
|
27091
|
+
show: showIndicator,
|
|
27092
|
+
isPlaying: indicatorIsPlaying
|
|
27093
|
+
},
|
|
27094
|
+
indicatorKeyRef.current
|
|
27095
|
+
)
|
|
27096
|
+
]
|
|
27097
|
+
}
|
|
27098
|
+
);
|
|
27099
|
+
});
|
|
27100
|
+
CroppedHlsVideoPlayer.displayName = "CroppedHlsVideoPlayer";
|
|
27101
|
+
var CroppedVideoPlayer = CroppedHlsVideoPlayer;
|
|
27102
|
+
var getSupabaseClient2 = () => {
|
|
27103
|
+
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
27104
|
+
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
27105
|
+
if (!url || !key) {
|
|
27106
|
+
throw new Error("Supabase configuration missing");
|
|
27107
|
+
}
|
|
27108
|
+
return supabaseJs.createClient(url, key);
|
|
27109
|
+
};
|
|
27110
|
+
var getAuthToken3 = async () => {
|
|
27111
|
+
try {
|
|
27112
|
+
const supabase = getSupabaseClient2();
|
|
27113
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
27114
|
+
return session?.access_token || null;
|
|
27115
|
+
} catch (error) {
|
|
27116
|
+
console.error("[useWorkspaceCrop] Error getting auth token:", error);
|
|
27117
|
+
return null;
|
|
27118
|
+
}
|
|
27119
|
+
};
|
|
27120
|
+
function useWorkspaceCrop(workspaceId) {
|
|
27121
|
+
const [crop, setCrop] = React23.useState(null);
|
|
27122
|
+
const [isLoading, setIsLoading] = React23.useState(true);
|
|
27123
|
+
const [error, setError] = React23.useState(null);
|
|
27124
|
+
React23.useEffect(() => {
|
|
27125
|
+
if (!workspaceId) {
|
|
27126
|
+
setIsLoading(false);
|
|
27127
|
+
return;
|
|
27128
|
+
}
|
|
27129
|
+
const fetchCrop = async () => {
|
|
27130
|
+
setIsLoading(true);
|
|
27131
|
+
setError(null);
|
|
27132
|
+
try {
|
|
27133
|
+
const token = await getAuthToken3();
|
|
27134
|
+
if (!token) {
|
|
27135
|
+
throw new Error("Authentication required");
|
|
27136
|
+
}
|
|
27137
|
+
const response = await fetch("/api/clips/supabase", {
|
|
27138
|
+
method: "POST",
|
|
27139
|
+
headers: {
|
|
27140
|
+
"Content-Type": "application/json",
|
|
27141
|
+
"Authorization": `Bearer ${token}`
|
|
27142
|
+
},
|
|
27143
|
+
body: JSON.stringify({
|
|
27144
|
+
action: "crop",
|
|
27145
|
+
workspaceId
|
|
27146
|
+
})
|
|
27147
|
+
});
|
|
27148
|
+
if (!response.ok) {
|
|
27149
|
+
throw new Error(`Failed to fetch crop: ${response.statusText}`);
|
|
27150
|
+
}
|
|
27151
|
+
const data = await response.json();
|
|
27152
|
+
console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
|
|
27153
|
+
setCrop(data.crop);
|
|
27154
|
+
} catch (err) {
|
|
27155
|
+
console.error("[useWorkspaceCrop] Error fetching crop:", err);
|
|
27156
|
+
setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
|
|
27157
|
+
setCrop(null);
|
|
27158
|
+
} finally {
|
|
27159
|
+
setIsLoading(false);
|
|
27160
|
+
}
|
|
27161
|
+
};
|
|
27162
|
+
fetchCrop();
|
|
27163
|
+
}, [workspaceId]);
|
|
27164
|
+
return { crop, isLoading, error };
|
|
27165
|
+
}
|
|
27166
|
+
function Skeleton({ className, ...props }) {
|
|
27167
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("animate-pulse rounded-md bg-muted", className), ...props });
|
|
27168
|
+
}
|
|
27169
|
+
var Select = SelectPrimitive__namespace.Root;
|
|
27170
|
+
var SelectGroup = SelectPrimitive__namespace.Group;
|
|
27171
|
+
var SelectValue = SelectPrimitive__namespace.Value;
|
|
27172
|
+
var SelectTrigger = React23__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27173
|
+
SelectPrimitive__namespace.Trigger,
|
|
27174
|
+
{
|
|
27175
|
+
ref,
|
|
27176
|
+
className: cn(
|
|
27177
|
+
"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",
|
|
27178
|
+
className
|
|
27179
|
+
),
|
|
27180
|
+
...props,
|
|
27181
|
+
children: [
|
|
27182
|
+
children,
|
|
27183
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
27184
|
+
]
|
|
27185
|
+
}
|
|
27186
|
+
));
|
|
27187
|
+
SelectTrigger.displayName = SelectPrimitive__namespace.Trigger.displayName;
|
|
27188
|
+
var SelectScrollUpButton = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27189
|
+
SelectPrimitive__namespace.ScrollUpButton,
|
|
27190
|
+
{
|
|
27191
|
+
ref,
|
|
27192
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
27193
|
+
...props,
|
|
27194
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4" })
|
|
27195
|
+
}
|
|
27196
|
+
));
|
|
27197
|
+
SelectScrollUpButton.displayName = SelectPrimitive__namespace.ScrollUpButton.displayName;
|
|
27198
|
+
var SelectScrollDownButton = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27199
|
+
SelectPrimitive__namespace.ScrollDownButton,
|
|
27200
|
+
{
|
|
27201
|
+
ref,
|
|
27202
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
27203
|
+
...props,
|
|
27204
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4" })
|
|
27205
|
+
}
|
|
27206
|
+
));
|
|
27207
|
+
SelectScrollDownButton.displayName = SelectPrimitive__namespace.ScrollDownButton.displayName;
|
|
27208
|
+
var SelectContent = React23__namespace.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27209
|
+
SelectPrimitive__namespace.Content,
|
|
27210
|
+
{
|
|
27211
|
+
ref,
|
|
27212
|
+
className: cn(
|
|
27213
|
+
"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]",
|
|
27214
|
+
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",
|
|
27215
|
+
className
|
|
27216
|
+
),
|
|
27217
|
+
position,
|
|
27218
|
+
...props,
|
|
27219
|
+
children: [
|
|
27220
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
|
|
27221
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27222
|
+
SelectPrimitive__namespace.Viewport,
|
|
27223
|
+
{
|
|
27224
|
+
className: cn(
|
|
27225
|
+
"p-1",
|
|
27226
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
27227
|
+
),
|
|
27228
|
+
children
|
|
27229
|
+
}
|
|
27230
|
+
),
|
|
27231
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
|
|
27232
|
+
]
|
|
27233
|
+
}
|
|
27234
|
+
) }));
|
|
27235
|
+
SelectContent.displayName = SelectPrimitive__namespace.Content.displayName;
|
|
27236
|
+
var SelectLabel = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27237
|
+
SelectPrimitive__namespace.Label,
|
|
27238
|
+
{
|
|
27239
|
+
ref,
|
|
27240
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", className),
|
|
27241
|
+
...props
|
|
27242
|
+
}
|
|
27243
|
+
));
|
|
27244
|
+
SelectLabel.displayName = SelectPrimitive__namespace.Label.displayName;
|
|
27245
|
+
var SelectItem = React23__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27246
|
+
SelectPrimitive__namespace.Item,
|
|
27247
|
+
{
|
|
27248
|
+
ref,
|
|
27249
|
+
className: cn(
|
|
27250
|
+
"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",
|
|
27251
|
+
className
|
|
27252
|
+
),
|
|
27253
|
+
...props,
|
|
27254
|
+
children: [
|
|
27255
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }) }),
|
|
27256
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
|
|
27257
|
+
]
|
|
27258
|
+
}
|
|
27259
|
+
));
|
|
27260
|
+
SelectItem.displayName = SelectPrimitive__namespace.Item.displayName;
|
|
27261
|
+
var SelectSeparator = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27262
|
+
SelectPrimitive__namespace.Separator,
|
|
27263
|
+
{
|
|
27264
|
+
ref,
|
|
27265
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
27266
|
+
...props
|
|
27267
|
+
}
|
|
27268
|
+
));
|
|
27269
|
+
SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
|
|
27270
|
+
var LoadingOverlay = ({
|
|
27271
|
+
isVisible,
|
|
27272
|
+
message = "Loading...",
|
|
27273
|
+
className
|
|
27274
|
+
}) => {
|
|
27275
|
+
if (!isVisible) return null;
|
|
27276
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
27277
|
+
motion.div,
|
|
27278
|
+
{
|
|
27279
|
+
initial: { opacity: 0 },
|
|
27280
|
+
animate: { opacity: 1 },
|
|
27281
|
+
exit: { opacity: 0 },
|
|
27282
|
+
transition: { duration: 0.2 },
|
|
27283
|
+
className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
|
|
27284
|
+
"aria-modal": "true",
|
|
27285
|
+
role: "dialog",
|
|
27286
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center space-y-3 rounded-lg bg-white p-8 shadow-xl", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message }) })
|
|
27287
|
+
}
|
|
27288
|
+
);
|
|
27289
|
+
};
|
|
27290
|
+
var LoadingOverlay_default = LoadingOverlay;
|
|
27291
|
+
var TimeDisplay = ({ className, variant = "default" }) => {
|
|
27292
|
+
const { dateTimeConfig } = useDashboardConfig();
|
|
27293
|
+
const [time2, setTime] = React23.useState("");
|
|
27294
|
+
const dbTimezone = useAppTimezone();
|
|
27295
|
+
const timezoneToDisplay = dbTimezone || dateTimeConfig?.defaultTimezone || "UTC";
|
|
27296
|
+
const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
|
|
27297
|
+
const timeSuffix = "";
|
|
27298
|
+
React23.useEffect(() => {
|
|
27299
|
+
const updateTime = () => {
|
|
27300
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
27301
|
+
const effectiveFormatOptions = {
|
|
27302
|
+
hour: "2-digit",
|
|
27303
|
+
minute: "2-digit",
|
|
27304
|
+
second: "2-digit",
|
|
27305
|
+
hour12: true,
|
|
27306
|
+
timeZone: timezoneToDisplay,
|
|
27307
|
+
...dateTimeConfig?.timeFormatOptions || {}
|
|
27308
|
+
// Allow override from config
|
|
27309
|
+
};
|
|
27310
|
+
try {
|
|
27311
|
+
setTime(new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2));
|
|
27312
|
+
} catch (e) {
|
|
27313
|
+
console.error("Error formatting time:", e);
|
|
27314
|
+
setTime("Error");
|
|
27315
|
+
}
|
|
27316
|
+
};
|
|
27317
|
+
updateTime();
|
|
27318
|
+
const interval = setInterval(updateTime, 1e3);
|
|
27319
|
+
return () => clearInterval(interval);
|
|
27320
|
+
}, [timezoneToDisplay, dateTimeConfig?.timeFormatOptions, localeToUse]);
|
|
27321
|
+
if (!time2) return null;
|
|
27322
|
+
if (variant === "minimal") {
|
|
27323
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: className || "", children: [
|
|
27324
|
+
time2,
|
|
27325
|
+
" ",
|
|
27326
|
+
timeSuffix
|
|
27327
|
+
] });
|
|
27328
|
+
}
|
|
27329
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27330
|
+
motion.div,
|
|
27331
|
+
{
|
|
27332
|
+
initial: { opacity: 0, y: -5 },
|
|
27333
|
+
animate: { opacity: 1, y: 0 },
|
|
27334
|
+
transition: { duration: 0.3 },
|
|
27335
|
+
className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
|
|
27336
|
+
children: [
|
|
27337
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
27338
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
|
|
27339
|
+
time2,
|
|
27340
|
+
" ",
|
|
27341
|
+
timeSuffix
|
|
27342
|
+
] })
|
|
27343
|
+
]
|
|
27344
|
+
}
|
|
27345
|
+
);
|
|
27346
|
+
};
|
|
27347
|
+
var TimeDisplay_default = TimeDisplay;
|
|
27348
|
+
var DateDisplay = ({ className, variant = "default" }) => {
|
|
27349
|
+
const { dateTimeConfig } = useDashboardConfig();
|
|
27350
|
+
const [date, setDate] = React23.useState("");
|
|
27351
|
+
const timezoneToDisplay = dateTimeConfig?.defaultTimezone || "UTC";
|
|
27352
|
+
const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
|
|
27353
|
+
React23.useEffect(() => {
|
|
27354
|
+
const getCurrentFormattedDate = () => {
|
|
27355
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
27356
|
+
const effectiveFormatOptions = {
|
|
27357
|
+
weekday: "short",
|
|
27358
|
+
day: "numeric",
|
|
27359
|
+
month: "short",
|
|
27360
|
+
timeZone: timezoneToDisplay,
|
|
27361
|
+
...dateTimeConfig?.dateFormatOptions || {}
|
|
27362
|
+
// Allow override from config
|
|
27363
|
+
};
|
|
27364
|
+
try {
|
|
27365
|
+
return new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2);
|
|
27366
|
+
} catch (e) {
|
|
27367
|
+
console.error("Error formatting date:", e);
|
|
27368
|
+
return "Error";
|
|
27369
|
+
}
|
|
27370
|
+
};
|
|
27371
|
+
const updateDate = () => {
|
|
27372
|
+
setDate(getCurrentFormattedDate());
|
|
27373
|
+
};
|
|
27374
|
+
updateDate();
|
|
27375
|
+
const interval = setInterval(() => {
|
|
27376
|
+
const currentDateStr = getCurrentFormattedDate();
|
|
27377
|
+
if (currentDateStr !== date) {
|
|
27378
|
+
updateDate();
|
|
27379
|
+
}
|
|
27380
|
+
}, 60 * 1e3);
|
|
27381
|
+
return () => clearInterval(interval);
|
|
27382
|
+
}, [date, timezoneToDisplay, dateTimeConfig?.dateFormatOptions, localeToUse]);
|
|
27383
|
+
if (!date) return null;
|
|
27384
|
+
if (variant === "minimal") {
|
|
27385
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: className || "", children: date });
|
|
27386
|
+
}
|
|
27387
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27388
|
+
motion.div,
|
|
27389
|
+
{
|
|
27390
|
+
initial: { opacity: 0, y: -5 },
|
|
27391
|
+
animate: { opacity: 1, y: 0 },
|
|
27392
|
+
transition: { duration: 0.3 },
|
|
27393
|
+
className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
|
|
27394
|
+
children: [
|
|
27395
|
+
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
|
|
27396
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-gray-800 tracking-tight", children: date })
|
|
27397
|
+
]
|
|
27398
|
+
}
|
|
27399
|
+
);
|
|
27400
|
+
};
|
|
27401
|
+
var DateDisplay_default = DateDisplay;
|
|
27402
|
+
var Card3 = Card2;
|
|
27403
|
+
var CardHeader3 = CardHeader2;
|
|
27404
|
+
var CardTitle3 = CardTitle2;
|
|
27405
|
+
var CardContent3 = CardContent2;
|
|
27406
|
+
var MetricCard2 = ({ title, value, unit = "", trend = null }) => {
|
|
27407
|
+
const getTrendColor = (trendValue) => {
|
|
27408
|
+
if (trendValue === null || trendValue === void 0) return "";
|
|
27409
|
+
return trendValue > 0 ? "text-green-500" : trendValue < 0 ? "text-red-500" : "text-gray-500";
|
|
27410
|
+
};
|
|
27411
|
+
const getTrendSymbol = (trendValue) => {
|
|
27412
|
+
if (trendValue === null || trendValue === void 0) return "";
|
|
27413
|
+
return trendValue > 0 ? "\u2191" : trendValue < 0 ? "\u2193" : "";
|
|
27414
|
+
};
|
|
27415
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card3, { className: "bg-white", children: [
|
|
27416
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHeader3, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle3, { className: "text-sm font-medium text-gray-500", children: title }) }),
|
|
27417
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardContent3, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
27418
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-semibold", children: [
|
|
27419
|
+
value,
|
|
27420
|
+
unit && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-sm text-gray-500", children: unit })
|
|
27421
|
+
] }),
|
|
27422
|
+
trend !== null && trend !== void 0 && // Check trend for null/undefined before accessing
|
|
27423
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center ${getTrendColor(trend)}`, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm", children: [
|
|
27424
|
+
getTrendSymbol(trend),
|
|
27425
|
+
" ",
|
|
27426
|
+
Math.abs(trend),
|
|
27427
|
+
"%"
|
|
27428
|
+
] }) })
|
|
27429
|
+
] }) })
|
|
27430
|
+
] });
|
|
27431
|
+
};
|
|
27432
|
+
var MetricCard_default = MetricCard2;
|
|
27433
|
+
var TimePickerDropdown = ({
|
|
27434
|
+
value,
|
|
27435
|
+
onChange,
|
|
27436
|
+
placeholder = "Select time",
|
|
27437
|
+
className = "",
|
|
27438
|
+
disabled = false
|
|
27439
|
+
}) => {
|
|
27440
|
+
const [isOpen, setIsOpen] = React23.useState(false);
|
|
27441
|
+
const [searchTerm, setSearchTerm] = React23.useState("");
|
|
27442
|
+
const dropdownRef = React23.useRef(null);
|
|
27443
|
+
const inputRef = React23.useRef(null);
|
|
27444
|
+
const generateTimeSlots = () => {
|
|
27445
|
+
const slots = [];
|
|
27446
|
+
for (let hour = 0; hour < 24; hour++) {
|
|
27447
|
+
for (let minute = 0; minute < 60; minute += 15) {
|
|
27448
|
+
const time24 = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
|
|
27449
|
+
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
27450
|
+
const ampm = hour < 12 ? "AM" : "PM";
|
|
27451
|
+
const time12 = `${hour12}:${minute.toString().padStart(2, "0")} ${ampm}`;
|
|
27452
|
+
slots.push({ value: time24, label: time12 });
|
|
27453
|
+
}
|
|
27454
|
+
}
|
|
27455
|
+
return slots;
|
|
27456
|
+
};
|
|
27457
|
+
const timeSlots = generateTimeSlots();
|
|
27458
|
+
const filteredSlots = timeSlots.filter(
|
|
27459
|
+
(slot) => slot.label.toLowerCase().includes(searchTerm.toLowerCase())
|
|
27460
|
+
);
|
|
27461
|
+
const getDisplayValue = (value2) => {
|
|
27462
|
+
if (!value2) return "";
|
|
27463
|
+
const normalizedValue = value2.substring(0, 5);
|
|
27464
|
+
const slot = timeSlots.find((s) => s.value === normalizedValue);
|
|
27465
|
+
return slot ? slot.label : value2;
|
|
27466
|
+
};
|
|
27467
|
+
React23.useEffect(() => {
|
|
27468
|
+
const handleClickOutside = (event) => {
|
|
27469
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
27470
|
+
setIsOpen(false);
|
|
27471
|
+
setSearchTerm("");
|
|
27472
|
+
}
|
|
27473
|
+
};
|
|
27474
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
27475
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
27476
|
+
}, []);
|
|
27477
|
+
const handleKeyDown = (e) => {
|
|
27478
|
+
if (e.key === "Escape") {
|
|
27479
|
+
setIsOpen(false);
|
|
27480
|
+
setSearchTerm("");
|
|
27481
|
+
} else if (e.key === "Enter") {
|
|
27482
|
+
e.preventDefault();
|
|
27483
|
+
if (filteredSlots.length > 0) {
|
|
27484
|
+
onChange(filteredSlots[0].value);
|
|
27485
|
+
setIsOpen(false);
|
|
27486
|
+
setSearchTerm("");
|
|
27229
27487
|
}
|
|
27230
27488
|
}
|
|
27231
|
-
|
|
27232
|
-
|
|
27233
|
-
|
|
27234
|
-
|
|
27235
|
-
|
|
27236
|
-
|
|
27237
|
-
|
|
27238
|
-
|
|
27239
|
-
|
|
27240
|
-
|
|
27241
|
-
|
|
27242
|
-
if (crop && !player.paused()) {
|
|
27243
|
-
renderFrameToCanvas();
|
|
27489
|
+
};
|
|
27490
|
+
const handleSelect = (timeValue) => {
|
|
27491
|
+
onChange(timeValue);
|
|
27492
|
+
setIsOpen(false);
|
|
27493
|
+
setSearchTerm("");
|
|
27494
|
+
};
|
|
27495
|
+
const handleToggle = () => {
|
|
27496
|
+
if (disabled) return;
|
|
27497
|
+
setIsOpen(!isOpen);
|
|
27498
|
+
if (!isOpen) {
|
|
27499
|
+
setTimeout(() => inputRef.current?.focus(), 100);
|
|
27244
27500
|
}
|
|
27245
|
-
|
|
27246
|
-
|
|
27247
|
-
|
|
27248
|
-
|
|
27249
|
-
|
|
27250
|
-
|
|
27251
|
-
|
|
27252
|
-
|
|
27253
|
-
|
|
27254
|
-
|
|
27255
|
-
|
|
27501
|
+
};
|
|
27502
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [
|
|
27503
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27504
|
+
"button",
|
|
27505
|
+
{
|
|
27506
|
+
type: "button",
|
|
27507
|
+
onClick: handleToggle,
|
|
27508
|
+
disabled,
|
|
27509
|
+
className: `
|
|
27510
|
+
w-full px-3 py-2 text-left bg-white border border-gray-300 rounded-md shadow-sm
|
|
27511
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
|
|
27512
|
+
hover:border-gray-400 transition-colors duration-200
|
|
27513
|
+
${disabled ? "bg-gray-50 cursor-not-allowed" : "cursor-pointer"}
|
|
27514
|
+
${isOpen ? "ring-2 ring-blue-500 border-blue-500" : ""}
|
|
27515
|
+
`,
|
|
27516
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
27517
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
27518
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-4 w-4 text-gray-400" }),
|
|
27519
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-sm ${value ? "text-gray-900" : "text-gray-500"}`, children: value ? getDisplayValue(value) : placeholder })
|
|
27520
|
+
] }),
|
|
27521
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27522
|
+
lucideReact.ChevronDown,
|
|
27523
|
+
{
|
|
27524
|
+
className: `h-4 w-4 text-gray-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
|
|
27525
|
+
}
|
|
27526
|
+
)
|
|
27527
|
+
] })
|
|
27528
|
+
}
|
|
27529
|
+
),
|
|
27530
|
+
isOpen && /* @__PURE__ */ jsxRuntime.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: [
|
|
27531
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
27532
|
+
"input",
|
|
27533
|
+
{
|
|
27534
|
+
ref: inputRef,
|
|
27535
|
+
type: "text",
|
|
27536
|
+
placeholder: "Search time...",
|
|
27537
|
+
value: searchTerm,
|
|
27538
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
27539
|
+
onKeyDown: handleKeyDown,
|
|
27540
|
+
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"
|
|
27541
|
+
}
|
|
27542
|
+
) }),
|
|
27543
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-y-auto max-h-48", children: filteredSlots.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-sm text-gray-500 text-center", children: "No times found" }) : filteredSlots.map((slot) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27544
|
+
"button",
|
|
27545
|
+
{
|
|
27546
|
+
type: "button",
|
|
27547
|
+
onClick: () => handleSelect(slot.value),
|
|
27548
|
+
className: `
|
|
27549
|
+
w-full px-3 py-2 text-left text-sm hover:bg-blue-50 hover:text-blue-600
|
|
27550
|
+
transition-colors duration-150 border-b border-gray-100 last:border-b-0
|
|
27551
|
+
${slot.value === value ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-700"}
|
|
27552
|
+
`,
|
|
27553
|
+
children: slot.label
|
|
27554
|
+
},
|
|
27555
|
+
slot.value
|
|
27556
|
+
)) })
|
|
27557
|
+
] })
|
|
27558
|
+
] });
|
|
27559
|
+
};
|
|
27560
|
+
var SilentErrorBoundary = class extends React23__namespace.default.Component {
|
|
27561
|
+
constructor(props) {
|
|
27562
|
+
super(props);
|
|
27563
|
+
this.handleClearAndReload = () => {
|
|
27564
|
+
console.log("[ErrorBoundary] User initiated reset");
|
|
27565
|
+
if (typeof window !== "undefined") {
|
|
27566
|
+
try {
|
|
27567
|
+
localStorage.clear();
|
|
27568
|
+
sessionStorage.clear();
|
|
27569
|
+
console.log("[ErrorBoundary] Cleared all storage");
|
|
27570
|
+
} catch (error) {
|
|
27571
|
+
console.error("[ErrorBoundary] Failed to clear storage:", error);
|
|
27572
|
+
}
|
|
27573
|
+
}
|
|
27574
|
+
window.location.href = "/login";
|
|
27256
27575
|
};
|
|
27257
|
-
|
|
27258
|
-
|
|
27259
|
-
|
|
27576
|
+
this.state = {
|
|
27577
|
+
hasError: false,
|
|
27578
|
+
errorCount: 0,
|
|
27579
|
+
lastError: null,
|
|
27580
|
+
errorInfo: null
|
|
27260
27581
|
};
|
|
27261
|
-
}
|
|
27262
|
-
|
|
27263
|
-
|
|
27264
|
-
|
|
27265
|
-
|
|
27266
|
-
|
|
27267
|
-
|
|
27268
|
-
|
|
27269
|
-
|
|
27270
|
-
|
|
27582
|
+
}
|
|
27583
|
+
static getDerivedStateFromError(error) {
|
|
27584
|
+
return { hasError: true };
|
|
27585
|
+
}
|
|
27586
|
+
componentDidCatch(error, errorInfo) {
|
|
27587
|
+
console.error("[ErrorBoundary] Caught render error:", {
|
|
27588
|
+
error: error.message,
|
|
27589
|
+
stack: error.stack,
|
|
27590
|
+
componentStack: errorInfo.componentStack,
|
|
27591
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
27592
|
+
});
|
|
27593
|
+
this.setState((prev) => ({
|
|
27594
|
+
errorCount: prev.errorCount + 1,
|
|
27595
|
+
lastError: error,
|
|
27596
|
+
errorInfo
|
|
27597
|
+
}));
|
|
27598
|
+
try {
|
|
27599
|
+
if (typeof window !== "undefined" && window.mixpanel) {
|
|
27600
|
+
window.mixpanel.track("React Render Error", {
|
|
27601
|
+
error: error.message,
|
|
27602
|
+
component: errorInfo.componentStack?.split("\n")[1] || "unknown",
|
|
27603
|
+
errorCount: this.state.errorCount + 1
|
|
27604
|
+
});
|
|
27271
27605
|
}
|
|
27606
|
+
} catch (analyticsError) {
|
|
27607
|
+
console.warn("[ErrorBoundary] Analytics tracking failed:", analyticsError);
|
|
27272
27608
|
}
|
|
27273
|
-
}, [videoProps.src, crop]);
|
|
27274
|
-
React23.useEffect(() => {
|
|
27275
|
-
return () => {
|
|
27276
|
-
stopCanvasRendering();
|
|
27277
|
-
};
|
|
27278
|
-
}, [stopCanvasRendering]);
|
|
27279
|
-
if (!crop) {
|
|
27280
|
-
return /* @__PURE__ */ jsxRuntime.jsx(VideoPlayer, { ref, ...videoProps, onClick });
|
|
27281
27609
|
}
|
|
27282
|
-
|
|
27283
|
-
if (!
|
|
27284
|
-
|
|
27285
|
-
const willBePlaying = player.paused();
|
|
27286
|
-
setIndicatorIsPlaying(willBePlaying);
|
|
27287
|
-
setShowIndicator(false);
|
|
27288
|
-
setTimeout(() => {
|
|
27289
|
-
indicatorKeyRef.current += 1;
|
|
27290
|
-
setShowIndicator(true);
|
|
27291
|
-
}, 0);
|
|
27292
|
-
onClick();
|
|
27293
|
-
};
|
|
27294
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27295
|
-
"div",
|
|
27296
|
-
{
|
|
27297
|
-
ref: videoContainerRef,
|
|
27298
|
-
className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""} ${videoProps.className || ""}`,
|
|
27299
|
-
onClick: handleClickWithIndicator,
|
|
27300
|
-
children: [
|
|
27301
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
27302
|
-
VideoPlayer,
|
|
27303
|
-
{
|
|
27304
|
-
ref: hiddenVideoRef,
|
|
27305
|
-
...videoProps,
|
|
27306
|
-
onReady: handleVideoReady,
|
|
27307
|
-
onPlay: handleVideoPlay,
|
|
27308
|
-
onPause: handleVideoPause,
|
|
27309
|
-
onEnded: handleVideoEnded,
|
|
27310
|
-
onSeeking: handleSeeking,
|
|
27311
|
-
onSeeked: handleSeeked,
|
|
27312
|
-
onLoadedMetadata: handleLoadedMetadata,
|
|
27313
|
-
onLoadedData: videoProps.onLoadedData,
|
|
27314
|
-
onPlaying: videoProps.onPlaying,
|
|
27315
|
-
onLoadingChange: videoProps.onLoadingChange
|
|
27316
|
-
}
|
|
27317
|
-
) }),
|
|
27318
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27319
|
-
"canvas",
|
|
27320
|
-
{
|
|
27321
|
-
ref: canvasRef,
|
|
27322
|
-
width: canvasDimensions.width,
|
|
27323
|
-
height: canvasDimensions.height,
|
|
27324
|
-
className: "max-w-full max-h-full",
|
|
27325
|
-
style: {
|
|
27326
|
-
display: isVideoReady ? "block" : "none",
|
|
27327
|
-
width: `${canvasDimensions.width}px`,
|
|
27328
|
-
height: `${canvasDimensions.height}px`
|
|
27329
|
-
}
|
|
27330
|
-
}
|
|
27331
|
-
),
|
|
27332
|
-
!isVideoReady && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
27333
|
-
debug && isVideoReady && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-2 left-2 bg-black/80 text-white text-xs p-2 rounded font-mono", children: [
|
|
27334
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
27335
|
-
"Crop: ",
|
|
27336
|
-
crop.x,
|
|
27337
|
-
",",
|
|
27338
|
-
crop.y,
|
|
27339
|
-
" ",
|
|
27340
|
-
crop.width,
|
|
27341
|
-
"x",
|
|
27342
|
-
crop.height,
|
|
27343
|
-
"%"
|
|
27344
|
-
] }),
|
|
27345
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
27346
|
-
"Canvas: ",
|
|
27347
|
-
canvasDimensions.width,
|
|
27348
|
-
"x",
|
|
27349
|
-
canvasDimensions.height,
|
|
27350
|
-
"px"
|
|
27351
|
-
] }),
|
|
27352
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
27353
|
-
"Processing: ",
|
|
27354
|
-
isProcessing ? "Yes" : "No"
|
|
27355
|
-
] })
|
|
27356
|
-
] }),
|
|
27357
|
-
onClick && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27358
|
-
PlayPauseIndicator,
|
|
27359
|
-
{
|
|
27360
|
-
show: showIndicator,
|
|
27361
|
-
isPlaying: indicatorIsPlaying
|
|
27362
|
-
},
|
|
27363
|
-
indicatorKeyRef.current
|
|
27364
|
-
)
|
|
27365
|
-
]
|
|
27610
|
+
render() {
|
|
27611
|
+
if (!this.state.hasError) {
|
|
27612
|
+
return this.props.children;
|
|
27366
27613
|
}
|
|
27367
|
-
|
|
27368
|
-
})
|
|
27369
|
-
|
|
27614
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-screen w-screen items-center justify-center bg-slate-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center space-y-6 text-center", children: [
|
|
27615
|
+
/* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading Dashboard..." }),
|
|
27616
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Taking longer than usual..." }),
|
|
27617
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
27618
|
+
"a",
|
|
27619
|
+
{
|
|
27620
|
+
href: "#",
|
|
27621
|
+
onClick: (e) => {
|
|
27622
|
+
e.preventDefault();
|
|
27623
|
+
this.handleClearAndReload();
|
|
27624
|
+
},
|
|
27625
|
+
className: "text-xs text-gray-400 hover:text-gray-600 underline transition-colors",
|
|
27626
|
+
children: "Reset and try again"
|
|
27627
|
+
}
|
|
27628
|
+
) }),
|
|
27629
|
+
process.env.NODE_ENV === "development" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-400 italic mt-4", children: "Check console for error details" })
|
|
27630
|
+
] }) });
|
|
27631
|
+
}
|
|
27632
|
+
};
|
|
27370
27633
|
var BackButton = ({
|
|
27371
27634
|
onClick,
|
|
27372
27635
|
text = "Back",
|
|
@@ -27731,70 +27994,6 @@ var NewClipsNotification = ({
|
|
|
27731
27994
|
}
|
|
27732
27995
|
);
|
|
27733
27996
|
};
|
|
27734
|
-
var getSupabaseClient2 = () => {
|
|
27735
|
-
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
27736
|
-
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
27737
|
-
if (!url || !key) {
|
|
27738
|
-
throw new Error("Supabase configuration missing");
|
|
27739
|
-
}
|
|
27740
|
-
return supabaseJs.createClient(url, key);
|
|
27741
|
-
};
|
|
27742
|
-
var getAuthToken3 = async () => {
|
|
27743
|
-
try {
|
|
27744
|
-
const supabase = getSupabaseClient2();
|
|
27745
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
27746
|
-
return session?.access_token || null;
|
|
27747
|
-
} catch (error) {
|
|
27748
|
-
console.error("[useWorkspaceCrop] Error getting auth token:", error);
|
|
27749
|
-
return null;
|
|
27750
|
-
}
|
|
27751
|
-
};
|
|
27752
|
-
function useWorkspaceCrop(workspaceId) {
|
|
27753
|
-
const [crop, setCrop] = React23.useState(null);
|
|
27754
|
-
const [isLoading, setIsLoading] = React23.useState(true);
|
|
27755
|
-
const [error, setError] = React23.useState(null);
|
|
27756
|
-
React23.useEffect(() => {
|
|
27757
|
-
if (!workspaceId) {
|
|
27758
|
-
setIsLoading(false);
|
|
27759
|
-
return;
|
|
27760
|
-
}
|
|
27761
|
-
const fetchCrop = async () => {
|
|
27762
|
-
setIsLoading(true);
|
|
27763
|
-
setError(null);
|
|
27764
|
-
try {
|
|
27765
|
-
const token = await getAuthToken3();
|
|
27766
|
-
if (!token) {
|
|
27767
|
-
throw new Error("Authentication required");
|
|
27768
|
-
}
|
|
27769
|
-
const response = await fetch("/api/clips/supabase", {
|
|
27770
|
-
method: "POST",
|
|
27771
|
-
headers: {
|
|
27772
|
-
"Content-Type": "application/json",
|
|
27773
|
-
"Authorization": `Bearer ${token}`
|
|
27774
|
-
},
|
|
27775
|
-
body: JSON.stringify({
|
|
27776
|
-
action: "crop",
|
|
27777
|
-
workspaceId
|
|
27778
|
-
})
|
|
27779
|
-
});
|
|
27780
|
-
if (!response.ok) {
|
|
27781
|
-
throw new Error(`Failed to fetch crop: ${response.statusText}`);
|
|
27782
|
-
}
|
|
27783
|
-
const data = await response.json();
|
|
27784
|
-
console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
|
|
27785
|
-
setCrop(data.crop);
|
|
27786
|
-
} catch (err) {
|
|
27787
|
-
console.error("[useWorkspaceCrop] Error fetching crop:", err);
|
|
27788
|
-
setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
|
|
27789
|
-
setCrop(null);
|
|
27790
|
-
} finally {
|
|
27791
|
-
setIsLoading(false);
|
|
27792
|
-
}
|
|
27793
|
-
};
|
|
27794
|
-
fetchCrop();
|
|
27795
|
-
}, [workspaceId]);
|
|
27796
|
-
return { crop, isLoading, error };
|
|
27797
|
-
}
|
|
27798
27997
|
var parseCycleTime = (value) => {
|
|
27799
27998
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
27800
27999
|
return value;
|
|
@@ -29050,6 +29249,11 @@ var BottlenecksContent = ({
|
|
|
29050
29249
|
return Number.isFinite(numericValue) ? numericValue : null;
|
|
29051
29250
|
})();
|
|
29052
29251
|
const videoRef = React23.useRef(null);
|
|
29252
|
+
const videoPlayerOptions = React23.useMemo(() => ({
|
|
29253
|
+
fluid: false,
|
|
29254
|
+
responsive: false,
|
|
29255
|
+
fill: false
|
|
29256
|
+
}), []);
|
|
29053
29257
|
const [initialFilter, setInitialFilter] = React23.useState("");
|
|
29054
29258
|
const currentIndexRef = React23.useRef(0);
|
|
29055
29259
|
const activeFilterRef = React23.useRef(initialFilter);
|
|
@@ -29060,6 +29264,7 @@ var BottlenecksContent = ({
|
|
|
29060
29264
|
const [duration, setDuration] = React23.useState(0);
|
|
29061
29265
|
const [currentIndex, setCurrentIndex] = React23.useState(0);
|
|
29062
29266
|
const [currentClipId, setCurrentClipId] = React23.useState(null);
|
|
29267
|
+
const [playbackSpeed, setPlaybackSpeed] = React23.useState(1);
|
|
29063
29268
|
const [isTransitioning, setIsTransitioning] = React23.useState(false);
|
|
29064
29269
|
const [pendingVideo, setPendingVideo] = React23.useState(null);
|
|
29065
29270
|
const [isVideoBuffering, setIsVideoBuffering] = React23.useState(false);
|
|
@@ -29077,6 +29282,31 @@ var BottlenecksContent = ({
|
|
|
29077
29282
|
const [categoryMetadata, setCategoryMetadata] = React23.useState([]);
|
|
29078
29283
|
const [currentMetadataIndex, setCurrentMetadataIndex] = React23.useState(0);
|
|
29079
29284
|
const [metadataCache, setMetadataCache] = React23.useState({});
|
|
29285
|
+
const invalidateMetadataCache = React23.useCallback((categories) => {
|
|
29286
|
+
setMetadataCache((prevCache) => {
|
|
29287
|
+
if (!prevCache || Object.keys(prevCache).length === 0) {
|
|
29288
|
+
return prevCache;
|
|
29289
|
+
}
|
|
29290
|
+
const targetCategories = categories ? (Array.isArray(categories) ? categories : [categories]).filter(Boolean) : null;
|
|
29291
|
+
let updatedCache = null;
|
|
29292
|
+
const shouldInvalidate = (key) => {
|
|
29293
|
+
if (!targetCategories || targetCategories.length === 0) {
|
|
29294
|
+
return true;
|
|
29295
|
+
}
|
|
29296
|
+
const [categoryId] = key.split("-");
|
|
29297
|
+
return targetCategories.includes(categoryId);
|
|
29298
|
+
};
|
|
29299
|
+
Object.keys(prevCache).forEach((cacheKey) => {
|
|
29300
|
+
if (shouldInvalidate(cacheKey)) {
|
|
29301
|
+
if (!updatedCache) {
|
|
29302
|
+
updatedCache = { ...prevCache };
|
|
29303
|
+
}
|
|
29304
|
+
delete updatedCache[cacheKey];
|
|
29305
|
+
}
|
|
29306
|
+
});
|
|
29307
|
+
return updatedCache || prevCache;
|
|
29308
|
+
});
|
|
29309
|
+
}, []);
|
|
29080
29310
|
const [triageClips, setTriageClips] = React23.useState([]);
|
|
29081
29311
|
const [isLoadingTriageClips, setIsLoadingTriageClips] = React23.useState(false);
|
|
29082
29312
|
const [isFullscreen, setIsFullscreen] = React23.useState(false);
|
|
@@ -29096,6 +29326,12 @@ var BottlenecksContent = ({
|
|
|
29096
29326
|
onNewClips: (notification) => {
|
|
29097
29327
|
console.log(`[BottlenecksContent] New clips detected:`, notification);
|
|
29098
29328
|
if (notification.clips.length > 0) {
|
|
29329
|
+
const categoryIds = notification.clips.map((clip) => clip.clip_type).filter(Boolean).map((value) => String(value));
|
|
29330
|
+
if (categoryIds.length > 0) {
|
|
29331
|
+
invalidateMetadataCache(categoryIds);
|
|
29332
|
+
} else {
|
|
29333
|
+
invalidateMetadataCache();
|
|
29334
|
+
}
|
|
29099
29335
|
fetchClipCounts();
|
|
29100
29336
|
}
|
|
29101
29337
|
}
|
|
@@ -29142,24 +29378,37 @@ var BottlenecksContent = ({
|
|
|
29142
29378
|
shift: shift || "0"
|
|
29143
29379
|
});
|
|
29144
29380
|
React23.useEffect(() => {
|
|
29145
|
-
if (clipTypes.length > 0
|
|
29146
|
-
const
|
|
29147
|
-
|
|
29148
|
-
|
|
29149
|
-
|
|
29150
|
-
|
|
29151
|
-
|
|
29152
|
-
|
|
29381
|
+
if (clipTypes.length > 0) {
|
|
29382
|
+
const currentFilterCount = initialFilter ? dynamicCounts[initialFilter] || 0 : 0;
|
|
29383
|
+
const hasAnyCounts = Object.values(dynamicCounts).some((c) => c > 0);
|
|
29384
|
+
const userHasNotNavigated = !initialFilter || activeFilterRef.current === initialFilter;
|
|
29385
|
+
const shouldRunSelection = !initialFilter || userHasNotNavigated && currentFilterCount === 0 && hasAnyCounts;
|
|
29386
|
+
if (shouldRunSelection) {
|
|
29387
|
+
let selectedType = null;
|
|
29388
|
+
if (clipTypes.length === 1) {
|
|
29389
|
+
selectedType = clipTypes[0];
|
|
29390
|
+
} else {
|
|
29391
|
+
const priorityOrder = ["cycle_completion", "fast-cycles", "slow-cycles", "idle_time"];
|
|
29392
|
+
for (const priorityType of priorityOrder) {
|
|
29393
|
+
const type = clipTypes.find((t) => t.type === priorityType && (dynamicCounts[t.type] || 0) > 0);
|
|
29394
|
+
if (type) {
|
|
29395
|
+
selectedType = type;
|
|
29396
|
+
break;
|
|
29397
|
+
}
|
|
29398
|
+
}
|
|
29399
|
+
if (!selectedType) {
|
|
29400
|
+
selectedType = clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0);
|
|
29401
|
+
}
|
|
29402
|
+
if (!selectedType) {
|
|
29403
|
+
selectedType = clipTypes[0];
|
|
29404
|
+
}
|
|
29405
|
+
}
|
|
29406
|
+
if (selectedType && selectedType.type !== initialFilter) {
|
|
29407
|
+
console.log(`[BottlenecksContent] Auto-selecting filter: ${selectedType.type} (count: ${dynamicCounts[selectedType.type] || 0})`);
|
|
29408
|
+
setInitialFilter(selectedType.type);
|
|
29409
|
+
setActiveFilter(selectedType.type);
|
|
29410
|
+
activeFilterRef.current = selectedType.type;
|
|
29153
29411
|
}
|
|
29154
|
-
}
|
|
29155
|
-
if (!selectedType) {
|
|
29156
|
-
selectedType = clipTypes.find((type) => (dynamicCounts[type.type] || 0) > 0);
|
|
29157
|
-
}
|
|
29158
|
-
const firstType = selectedType || clipTypes[0];
|
|
29159
|
-
if (firstType) {
|
|
29160
|
-
setInitialFilter(firstType.type);
|
|
29161
|
-
setActiveFilter(firstType.type);
|
|
29162
|
-
activeFilterRef.current = firstType.type;
|
|
29163
29412
|
}
|
|
29164
29413
|
}
|
|
29165
29414
|
}, [clipTypes, dynamicCounts, initialFilter]);
|
|
@@ -29211,7 +29460,7 @@ var BottlenecksContent = ({
|
|
|
29211
29460
|
} finally {
|
|
29212
29461
|
fetchInProgressRef.current.delete(operationKey);
|
|
29213
29462
|
}
|
|
29214
|
-
}, [workspaceId, date, s3ClipsService, effectiveShift, dashboardConfig, updateClipCounts]);
|
|
29463
|
+
}, [workspaceId, date, s3ClipsService, effectiveShift, dashboardConfig, updateClipCounts, timezone, totalOutput]);
|
|
29215
29464
|
const loadingCategoryRef = React23.useRef(null);
|
|
29216
29465
|
const loadFirstVideoForCategory = React23.useCallback(async (category) => {
|
|
29217
29466
|
if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
|
|
@@ -29301,15 +29550,16 @@ var BottlenecksContent = ({
|
|
|
29301
29550
|
loadingCategoryRef.current = null;
|
|
29302
29551
|
fetchInProgressRef.current.delete(operationKey);
|
|
29303
29552
|
}
|
|
29304
|
-
}, [workspaceId, date, s3ClipsService, mergedCounts, effectiveShift]);
|
|
29553
|
+
}, [workspaceId, date, s3ClipsService, mergedCounts, effectiveShift, timezone]);
|
|
29305
29554
|
const handleRefreshClips = React23.useCallback(async () => {
|
|
29306
29555
|
console.log("[BottlenecksContent] Refreshing clips after new additions");
|
|
29307
29556
|
acknowledgeNewClips();
|
|
29557
|
+
invalidateMetadataCache();
|
|
29308
29558
|
await fetchClipCounts();
|
|
29309
29559
|
if (activeFilter && mergedCounts[activeFilter] > 0) {
|
|
29310
29560
|
await loadFirstVideoForCategory(activeFilter);
|
|
29311
29561
|
}
|
|
29312
|
-
}, [acknowledgeNewClips, fetchClipCounts, activeFilter, mergedCounts, loadFirstVideoForCategory]);
|
|
29562
|
+
}, [acknowledgeNewClips, fetchClipCounts, activeFilter, mergedCounts, loadFirstVideoForCategory, invalidateMetadataCache]);
|
|
29313
29563
|
React23.useEffect(() => {
|
|
29314
29564
|
if (s3ClipsService) {
|
|
29315
29565
|
fetchClipCounts();
|
|
@@ -29479,38 +29729,38 @@ var BottlenecksContent = ({
|
|
|
29479
29729
|
loadingTimeoutRef.current = null;
|
|
29480
29730
|
}
|
|
29481
29731
|
}, []);
|
|
29482
|
-
const loadCategoryMetadata = React23.useCallback(async (categoryId, autoLoadFirstVideo = false) => {
|
|
29732
|
+
const loadCategoryMetadata = React23.useCallback(async (categoryId, autoLoadFirstVideo = false, forceRefresh = false) => {
|
|
29483
29733
|
if (!workspaceId) {
|
|
29484
29734
|
return;
|
|
29485
29735
|
}
|
|
29486
|
-
const
|
|
29487
|
-
|
|
29488
|
-
|
|
29489
|
-
|
|
29490
|
-
|
|
29491
|
-
|
|
29492
|
-
|
|
29493
|
-
|
|
29494
|
-
|
|
29495
|
-
|
|
29496
|
-
|
|
29497
|
-
|
|
29498
|
-
|
|
29499
|
-
|
|
29500
|
-
|
|
29501
|
-
|
|
29502
|
-
|
|
29736
|
+
const resolvedDate = date || getOperationalDate(timezone);
|
|
29737
|
+
const cacheKey = `${categoryId}-${resolvedDate}-${effectiveShift}`;
|
|
29738
|
+
const cachedMetadata = !forceRefresh ? metadataCache[cacheKey] : void 0;
|
|
29739
|
+
try {
|
|
29740
|
+
if (cachedMetadata) {
|
|
29741
|
+
console.log(`[BottlenecksContent] Using cached metadata for ${categoryId}`);
|
|
29742
|
+
setCategoryMetadata(cachedMetadata);
|
|
29743
|
+
categoryMetadataRef.current = cachedMetadata;
|
|
29744
|
+
if (autoLoadFirstVideo && cachedMetadata.length > 0 && s3ClipsService) {
|
|
29745
|
+
const firstClipMeta = cachedMetadata[0];
|
|
29746
|
+
try {
|
|
29747
|
+
const video = await s3ClipsService.getClipById(firstClipMeta.clipId);
|
|
29748
|
+
if (video && isMountedRef.current) {
|
|
29749
|
+
setCurrentClipId(firstClipMeta.clipId);
|
|
29750
|
+
setAllVideos([video]);
|
|
29751
|
+
setCurrentIndex(0);
|
|
29752
|
+
setCurrentMetadataIndex(0);
|
|
29753
|
+
currentMetadataIndexRef.current = 0;
|
|
29754
|
+
console.log(`[BottlenecksContent] Auto-loaded first video from cache: ${video.id} (1/${cachedMetadata.length})`);
|
|
29755
|
+
}
|
|
29756
|
+
} catch (error2) {
|
|
29757
|
+
console.error(`[BottlenecksContent] Error loading first video from cache:`, error2);
|
|
29758
|
+
clearLoadingState();
|
|
29503
29759
|
}
|
|
29504
|
-
} catch (error2) {
|
|
29505
|
-
console.error(`[BottlenecksContent] Error loading first video from cache:`, error2);
|
|
29506
|
-
setIsCategoryLoading(false);
|
|
29507
|
-
clearLoadingState();
|
|
29508
29760
|
}
|
|
29761
|
+
return;
|
|
29509
29762
|
}
|
|
29510
|
-
|
|
29511
|
-
}
|
|
29512
|
-
try {
|
|
29513
|
-
console.log(`[BottlenecksContent] Loading metadata for category: ${categoryId}`);
|
|
29763
|
+
console.log(`[BottlenecksContent] Loading metadata for category: ${categoryId}${forceRefresh ? " (force refresh)" : ""}`);
|
|
29514
29764
|
const { createClient: createClient5 } = await import('@supabase/supabase-js');
|
|
29515
29765
|
const supabase = createClient5(
|
|
29516
29766
|
process.env.NEXT_PUBLIC_SUPABASE_URL || "",
|
|
@@ -29535,8 +29785,8 @@ var BottlenecksContent = ({
|
|
|
29535
29785
|
action: "percentile-clips",
|
|
29536
29786
|
percentileAction: percentileType,
|
|
29537
29787
|
workspaceId,
|
|
29538
|
-
startDate: `${
|
|
29539
|
-
endDate: `${
|
|
29788
|
+
startDate: `${resolvedDate}T00:00:00Z`,
|
|
29789
|
+
endDate: `${resolvedDate}T23:59:59Z`,
|
|
29540
29790
|
percentile: 10,
|
|
29541
29791
|
shiftId: effectiveShift,
|
|
29542
29792
|
limit: 100
|
|
@@ -29552,7 +29802,7 @@ var BottlenecksContent = ({
|
|
|
29552
29802
|
body: JSON.stringify({
|
|
29553
29803
|
action: "clip-metadata",
|
|
29554
29804
|
workspaceId,
|
|
29555
|
-
date:
|
|
29805
|
+
date: resolvedDate,
|
|
29556
29806
|
shift: effectiveShift,
|
|
29557
29807
|
category: categoryId,
|
|
29558
29808
|
page: 1,
|
|
@@ -29597,19 +29847,22 @@ var BottlenecksContent = ({
|
|
|
29597
29847
|
setCurrentIndex(0);
|
|
29598
29848
|
setCurrentMetadataIndex(0);
|
|
29599
29849
|
currentMetadataIndexRef.current = 0;
|
|
29600
|
-
setIsCategoryLoading(false);
|
|
29601
29850
|
console.log(`[BottlenecksContent] Auto-loaded first video: ${video.id} (1/${metadataClips.length})`);
|
|
29602
29851
|
}
|
|
29603
29852
|
} catch (error2) {
|
|
29604
29853
|
console.error(`[BottlenecksContent] Error loading first video:`, error2);
|
|
29605
|
-
setIsCategoryLoading(false);
|
|
29606
29854
|
}
|
|
29607
29855
|
}
|
|
29856
|
+
} else {
|
|
29857
|
+
setCategoryMetadata([]);
|
|
29858
|
+
categoryMetadataRef.current = [];
|
|
29608
29859
|
}
|
|
29609
29860
|
} catch (error2) {
|
|
29610
29861
|
console.error(`[BottlenecksContent] Error loading category metadata:`, error2);
|
|
29862
|
+
} finally {
|
|
29863
|
+
setIsCategoryLoading(false);
|
|
29611
29864
|
}
|
|
29612
|
-
}, [workspaceId, date, effectiveShift, isPercentileCategory, metadataCache, s3ClipsService]);
|
|
29865
|
+
}, [workspaceId, date, effectiveShift, isPercentileCategory, metadataCache, s3ClipsService, timezone, clearLoadingState]);
|
|
29613
29866
|
const loadAndPlayClipById = React23.useCallback(async (clipId, categoryId, position) => {
|
|
29614
29867
|
if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
|
|
29615
29868
|
console.log(`[BottlenecksContent] Loading clip by ID: ${clipId}, category=${categoryId}, position=${position}`);
|
|
@@ -29633,21 +29886,31 @@ var BottlenecksContent = ({
|
|
|
29633
29886
|
}
|
|
29634
29887
|
try {
|
|
29635
29888
|
await loadCategoryMetadata(categoryId, false);
|
|
29636
|
-
|
|
29637
|
-
|
|
29638
|
-
|
|
29639
|
-
|
|
29640
|
-
|
|
29641
|
-
|
|
29642
|
-
|
|
29643
|
-
|
|
29644
|
-
|
|
29645
|
-
|
|
29646
|
-
|
|
29647
|
-
|
|
29648
|
-
|
|
29649
|
-
|
|
29650
|
-
|
|
29889
|
+
let metadataArray = categoryMetadataRef.current;
|
|
29890
|
+
const clipExistsInMetadata = metadataArray.some((clip) => clip.clipId === clipId);
|
|
29891
|
+
if (metadataArray.length === 0 || !clipExistsInMetadata) {
|
|
29892
|
+
console.warn(`[BottlenecksContent] Clip ${clipId} not found in metadata for ${categoryId} (cache hit: ${metadataArray.length > 0}) - forcing refresh`);
|
|
29893
|
+
await loadCategoryMetadata(categoryId, false, true);
|
|
29894
|
+
metadataArray = categoryMetadataRef.current;
|
|
29895
|
+
}
|
|
29896
|
+
if (metadataArray.length === 0) {
|
|
29897
|
+
throw new Error(`No metadata available for category ${categoryId}`);
|
|
29898
|
+
}
|
|
29899
|
+
const clickedClipIndex = metadataArray.findIndex((clip) => clip.clipId === clipId);
|
|
29900
|
+
if (clickedClipIndex === -1) {
|
|
29901
|
+
throw new Error(`Clip ${clipId} not found after metadata refresh`);
|
|
29902
|
+
}
|
|
29903
|
+
setCurrentMetadataIndex(clickedClipIndex);
|
|
29904
|
+
currentMetadataIndexRef.current = clickedClipIndex;
|
|
29905
|
+
const video = await s3ClipsService.getClipById(clipId);
|
|
29906
|
+
if (video) {
|
|
29907
|
+
setPendingVideo(video);
|
|
29908
|
+
setCurrentClipId(clipId);
|
|
29909
|
+
setAllVideos([video]);
|
|
29910
|
+
setCurrentIndex(0);
|
|
29911
|
+
console.log(`[BottlenecksContent] Loaded clip ${clipId} (${clickedClipIndex + 1}/${metadataArray.length})`);
|
|
29912
|
+
} else {
|
|
29913
|
+
throw new Error(`Failed to load video data for clip ${clipId}`);
|
|
29651
29914
|
}
|
|
29652
29915
|
} catch (error2) {
|
|
29653
29916
|
console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
|
|
@@ -29661,7 +29924,7 @@ var BottlenecksContent = ({
|
|
|
29661
29924
|
clearLoadingState();
|
|
29662
29925
|
}
|
|
29663
29926
|
}
|
|
29664
|
-
}, [workspaceId, s3ClipsService,
|
|
29927
|
+
}, [workspaceId, s3ClipsService, updateActiveFilter, clearLoadingState, loadCategoryMetadata]);
|
|
29665
29928
|
React23.useCallback(async (categoryId, clipIndex) => {
|
|
29666
29929
|
console.warn("[BottlenecksContent] loadAndPlayClip is deprecated, use loadAndPlayClipById instead");
|
|
29667
29930
|
if (!workspaceId || !s3ClipsService || !isMountedRef.current) return;
|
|
@@ -29688,7 +29951,7 @@ var BottlenecksContent = ({
|
|
|
29688
29951
|
});
|
|
29689
29952
|
setIsNavigating(false);
|
|
29690
29953
|
}
|
|
29691
|
-
}, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById]);
|
|
29954
|
+
}, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById, timezone]);
|
|
29692
29955
|
const handleNext = React23.useCallback(async () => {
|
|
29693
29956
|
if (!isMountedRef.current) return;
|
|
29694
29957
|
const currentFilter = activeFilterRef.current;
|
|
@@ -29705,8 +29968,18 @@ var BottlenecksContent = ({
|
|
|
29705
29968
|
}
|
|
29706
29969
|
try {
|
|
29707
29970
|
const currentMetaIndex = currentMetadataIndexRef.current;
|
|
29708
|
-
|
|
29971
|
+
let metadataArray = categoryMetadataRef.current;
|
|
29972
|
+
if (metadataArray.length === 0) {
|
|
29973
|
+
console.log(`[handleNext] Metadata empty for ${currentFilter}, loading before navigation`);
|
|
29974
|
+
await loadCategoryMetadata(currentFilter, false);
|
|
29975
|
+
metadataArray = categoryMetadataRef.current;
|
|
29976
|
+
}
|
|
29709
29977
|
console.log(`[handleNext] Unified navigation: ${currentFilter}, metadata index: ${currentMetaIndex}/${metadataArray.length}`);
|
|
29978
|
+
if (metadataArray.length === 0) {
|
|
29979
|
+
console.warn("[handleNext] No metadata available after refresh - stopping navigation");
|
|
29980
|
+
clearLoadingState();
|
|
29981
|
+
return;
|
|
29982
|
+
}
|
|
29710
29983
|
if (currentMetaIndex < metadataArray.length - 1) {
|
|
29711
29984
|
const nextMetadataIndex = currentMetaIndex + 1;
|
|
29712
29985
|
const nextClipMeta = metadataArray[nextMetadataIndex];
|
|
@@ -29741,7 +30014,7 @@ var BottlenecksContent = ({
|
|
|
29741
30014
|
});
|
|
29742
30015
|
clearLoadingState();
|
|
29743
30016
|
}
|
|
29744
|
-
}, [clearLoadingState, s3ClipsService]);
|
|
30017
|
+
}, [clearLoadingState, s3ClipsService, loadCategoryMetadata]);
|
|
29745
30018
|
const handlePrevious = React23.useCallback(async () => {
|
|
29746
30019
|
if (!isMountedRef.current) return;
|
|
29747
30020
|
const currentFilter = activeFilterRef.current;
|
|
@@ -29758,8 +30031,18 @@ var BottlenecksContent = ({
|
|
|
29758
30031
|
}
|
|
29759
30032
|
try {
|
|
29760
30033
|
const currentMetaIndex = currentMetadataIndexRef.current;
|
|
29761
|
-
|
|
30034
|
+
let metadataArray = categoryMetadataRef.current;
|
|
30035
|
+
if (metadataArray.length === 0) {
|
|
30036
|
+
console.log(`[handlePrevious] Metadata empty for ${currentFilter}, loading before navigation`);
|
|
30037
|
+
await loadCategoryMetadata(currentFilter, false);
|
|
30038
|
+
metadataArray = categoryMetadataRef.current;
|
|
30039
|
+
}
|
|
29762
30040
|
console.log(`[handlePrevious] Unified navigation: ${currentFilter}, metadata index: ${currentMetaIndex}/${metadataArray.length}`);
|
|
30041
|
+
if (metadataArray.length === 0) {
|
|
30042
|
+
console.warn("[handlePrevious] No metadata available after refresh - stopping navigation");
|
|
30043
|
+
clearLoadingState();
|
|
30044
|
+
return;
|
|
30045
|
+
}
|
|
29763
30046
|
if (currentMetaIndex > 0) {
|
|
29764
30047
|
const prevMetadataIndex = currentMetaIndex - 1;
|
|
29765
30048
|
const prevClipMeta = metadataArray[prevMetadataIndex];
|
|
@@ -29790,7 +30073,7 @@ var BottlenecksContent = ({
|
|
|
29790
30073
|
});
|
|
29791
30074
|
clearLoadingState();
|
|
29792
30075
|
}
|
|
29793
|
-
}, [clearLoadingState, s3ClipsService]);
|
|
30076
|
+
}, [clearLoadingState, s3ClipsService, loadCategoryMetadata]);
|
|
29794
30077
|
const currentVideo = React23.useMemo(() => {
|
|
29795
30078
|
if (!filteredVideos || filteredVideos.length === 0 || currentIndex >= filteredVideos.length) {
|
|
29796
30079
|
return null;
|
|
@@ -29803,7 +30086,10 @@ var BottlenecksContent = ({
|
|
|
29803
30086
|
if (error?.isRetrying) {
|
|
29804
30087
|
setError(null);
|
|
29805
30088
|
}
|
|
29806
|
-
|
|
30089
|
+
if (videoRef.current?.playbackRate && playbackSpeed !== 1) {
|
|
30090
|
+
videoRef.current.playbackRate(playbackSpeed);
|
|
30091
|
+
}
|
|
30092
|
+
}, [error, playbackSpeed]);
|
|
29807
30093
|
const handleVideoPlay = React23.useCallback(async (player) => {
|
|
29808
30094
|
setIsPlaying(true);
|
|
29809
30095
|
setIsInitialLoading(false);
|
|
@@ -29973,7 +30259,13 @@ var BottlenecksContent = ({
|
|
|
29973
30259
|
player.pause();
|
|
29974
30260
|
}
|
|
29975
30261
|
};
|
|
29976
|
-
const
|
|
30262
|
+
const handlePlaybackSpeedChange = React23.useCallback((speed) => {
|
|
30263
|
+
setPlaybackSpeed(speed);
|
|
30264
|
+
if (videoRef.current?.playbackRate) {
|
|
30265
|
+
videoRef.current.playbackRate(speed);
|
|
30266
|
+
}
|
|
30267
|
+
}, []);
|
|
30268
|
+
React23.useCallback((e) => {
|
|
29977
30269
|
e.stopPropagation();
|
|
29978
30270
|
setIsFullscreen((prev) => !prev);
|
|
29979
30271
|
}, []);
|
|
@@ -30147,12 +30439,7 @@ var BottlenecksContent = ({
|
|
|
30147
30439
|
onLoadedData: handleLoadedData,
|
|
30148
30440
|
onPlaying: handleVideoPlaying,
|
|
30149
30441
|
onLoadingChange: handleVideoLoadingChange,
|
|
30150
|
-
options:
|
|
30151
|
-
// Ensure full height is always visible - no cropping
|
|
30152
|
-
fluid: false,
|
|
30153
|
-
responsive: false,
|
|
30154
|
-
fill: false
|
|
30155
|
-
}
|
|
30442
|
+
options: videoPlayerOptions
|
|
30156
30443
|
}
|
|
30157
30444
|
)
|
|
30158
30445
|
}
|
|
@@ -30213,58 +30500,7 @@ var BottlenecksContent = ({
|
|
|
30213
30500
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
|
|
30214
30501
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
|
|
30215
30502
|
] }) })
|
|
30216
|
-
)
|
|
30217
|
-
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
30218
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
30219
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30220
|
-
"button",
|
|
30221
|
-
{
|
|
30222
|
-
onClick: (e) => {
|
|
30223
|
-
e.stopPropagation();
|
|
30224
|
-
togglePlayback();
|
|
30225
|
-
},
|
|
30226
|
-
className: "p-1.5 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
30227
|
-
"aria-label": isPlaying ? "Pause" : "Play",
|
|
30228
|
-
children: isPlaying ? /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.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" }) })
|
|
30229
|
-
}
|
|
30230
|
-
),
|
|
30231
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-mono px-2", children: [
|
|
30232
|
-
formatTime2(currentTime),
|
|
30233
|
-
" / ",
|
|
30234
|
-
formatTime2(duration)
|
|
30235
|
-
] })
|
|
30236
|
-
] }),
|
|
30237
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30238
|
-
"input",
|
|
30239
|
-
{
|
|
30240
|
-
type: "range",
|
|
30241
|
-
min: "0",
|
|
30242
|
-
max: duration || 0,
|
|
30243
|
-
value: currentTime,
|
|
30244
|
-
onChange: (e) => {
|
|
30245
|
-
if (videoRef.current) {
|
|
30246
|
-
videoRef.current.currentTime(Number(e.target.value));
|
|
30247
|
-
}
|
|
30248
|
-
},
|
|
30249
|
-
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",
|
|
30250
|
-
style: {
|
|
30251
|
-
WebkitAppearance: "none",
|
|
30252
|
-
appearance: "none"
|
|
30253
|
-
},
|
|
30254
|
-
"aria-label": "Seek slider"
|
|
30255
|
-
}
|
|
30256
|
-
),
|
|
30257
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30258
|
-
"button",
|
|
30259
|
-
{
|
|
30260
|
-
onClick: toggleFullscreen,
|
|
30261
|
-
className: "p-1.5 hover:bg-white/20 rounded-full focus:outline-none focus:ring-2 focus:ring-white/50",
|
|
30262
|
-
"aria-label": "Fullscreen",
|
|
30263
|
-
title: "Expand to fullscreen",
|
|
30264
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { className: "h-5 w-5" })
|
|
30265
|
-
}
|
|
30266
|
-
)
|
|
30267
|
-
] }) })
|
|
30503
|
+
)
|
|
30268
30504
|
] }) }) }) : (
|
|
30269
30505
|
/* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
|
|
30270
30506
|
hasInitialLoad && Object.keys(mergedCounts).length > 0 && Object.values(mergedCounts).every((count) => count === 0) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center justify-center ${triageMode ? "h-full" : "h-[calc(100%-4rem)]"}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
|
|
@@ -30482,11 +30718,7 @@ var BottlenecksContent = ({
|
|
|
30482
30718
|
onLoadedData: handleLoadedData,
|
|
30483
30719
|
onPlaying: handleVideoPlaying,
|
|
30484
30720
|
onLoadingChange: handleVideoLoadingChange,
|
|
30485
|
-
options:
|
|
30486
|
-
fluid: false,
|
|
30487
|
-
responsive: false,
|
|
30488
|
-
fill: false
|
|
30489
|
-
}
|
|
30721
|
+
options: videoPlayerOptions
|
|
30490
30722
|
}
|
|
30491
30723
|
)
|
|
30492
30724
|
}
|
|
@@ -30494,45 +30726,32 @@ var BottlenecksContent = ({
|
|
|
30494
30726
|
(isTransitioning || isVideoBuffering && isInitialLoading) && !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
30495
30727
|
!isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
30496
30728
|
/* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
30497
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-
|
|
30498
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30499
|
-
|
|
30729
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
30730
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Speed:" }),
|
|
30731
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
30732
|
+
"select",
|
|
30500
30733
|
{
|
|
30501
|
-
|
|
30502
|
-
|
|
30503
|
-
|
|
30504
|
-
|
|
30505
|
-
|
|
30506
|
-
|
|
30507
|
-
|
|
30734
|
+
value: playbackSpeed,
|
|
30735
|
+
onChange: (e) => handlePlaybackSpeedChange(Number(e.target.value)),
|
|
30736
|
+
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",
|
|
30737
|
+
"aria-label": "Playback speed",
|
|
30738
|
+
children: [
|
|
30739
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "0.25", children: "0.25x" }),
|
|
30740
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "0.5", children: "0.5x" }),
|
|
30741
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "0.75", children: "0.75x" }),
|
|
30742
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "1", children: "1x" }),
|
|
30743
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "1.25", children: "1.25x" }),
|
|
30744
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "1.5", children: "1.5x" }),
|
|
30745
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "1.75", children: "1.75x" }),
|
|
30746
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "2", children: "2x" }),
|
|
30747
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "3", children: "3x" }),
|
|
30748
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "4", children: "4x" }),
|
|
30749
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "5", children: "5x" }),
|
|
30750
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "10", children: "10x" })
|
|
30751
|
+
]
|
|
30508
30752
|
}
|
|
30509
|
-
)
|
|
30510
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-mono px-2", children: [
|
|
30511
|
-
formatTime2(currentTime),
|
|
30512
|
-
" / ",
|
|
30513
|
-
formatTime2(duration)
|
|
30514
|
-
] })
|
|
30753
|
+
)
|
|
30515
30754
|
] }),
|
|
30516
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30517
|
-
"input",
|
|
30518
|
-
{
|
|
30519
|
-
type: "range",
|
|
30520
|
-
min: "0",
|
|
30521
|
-
max: duration || 0,
|
|
30522
|
-
value: currentTime,
|
|
30523
|
-
onChange: (e) => {
|
|
30524
|
-
if (videoRef.current) {
|
|
30525
|
-
videoRef.current.currentTime(Number(e.target.value));
|
|
30526
|
-
}
|
|
30527
|
-
},
|
|
30528
|
-
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",
|
|
30529
|
-
style: {
|
|
30530
|
-
WebkitAppearance: "none",
|
|
30531
|
-
appearance: "none"
|
|
30532
|
-
},
|
|
30533
|
-
"aria-label": "Seek slider"
|
|
30534
|
-
}
|
|
30535
|
-
),
|
|
30536
30755
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
30537
30756
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30538
30757
|
"button",
|
|
@@ -37633,26 +37852,26 @@ var SingleVideoStream = ({
|
|
|
37633
37852
|
const hlsStreamUrl = streamUrl || getCameraStreamUrl(workspaceName, baseUrl);
|
|
37634
37853
|
console.log(`Using camera URL for ${workspaceName}: ${hlsStreamUrl}`);
|
|
37635
37854
|
const mergedHlsConfig = { ...DEFAULT_HLS_CONFIG, ...hlsConfig };
|
|
37636
|
-
if (
|
|
37637
|
-
const hls = new
|
|
37855
|
+
if (Hls3__default.default.isSupported()) {
|
|
37856
|
+
const hls = new Hls3__default.default(mergedHlsConfig);
|
|
37638
37857
|
hlsRef.current = hls;
|
|
37639
|
-
hls.on(
|
|
37858
|
+
hls.on(Hls3__default.default.Events.MEDIA_ATTACHED, () => {
|
|
37640
37859
|
console.log("HLS media attached");
|
|
37641
37860
|
hls.loadSource(hlsStreamUrl);
|
|
37642
37861
|
});
|
|
37643
|
-
hls.on(
|
|
37862
|
+
hls.on(Hls3__default.default.Events.MANIFEST_PARSED, () => {
|
|
37644
37863
|
console.log("HLS manifest parsed");
|
|
37645
37864
|
attemptPlay(video);
|
|
37646
37865
|
});
|
|
37647
|
-
hls.on(
|
|
37866
|
+
hls.on(Hls3__default.default.Events.ERROR, (_, data) => {
|
|
37648
37867
|
if (data.fatal) {
|
|
37649
37868
|
console.error("Fatal HLS error:", data.type, data.details);
|
|
37650
37869
|
switch (data.type) {
|
|
37651
|
-
case
|
|
37870
|
+
case Hls3__default.default.ErrorTypes.NETWORK_ERROR:
|
|
37652
37871
|
console.error("Fatal network error encountered");
|
|
37653
37872
|
setError("Network error: Please check your connection");
|
|
37654
37873
|
break;
|
|
37655
|
-
case
|
|
37874
|
+
case Hls3__default.default.ErrorTypes.MEDIA_ERROR:
|
|
37656
37875
|
console.error("Fatal media error encountered, trying to recover");
|
|
37657
37876
|
hls.recoverMediaError();
|
|
37658
37877
|
break;
|
|
@@ -42413,9 +42632,9 @@ var MetricCards = React23.memo(({ lineInfo }) => {
|
|
|
42413
42632
|
animate: "animate",
|
|
42414
42633
|
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",
|
|
42415
42634
|
children: [
|
|
42416
|
-
/* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[
|
|
42417
|
-
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 mb-
|
|
42418
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100%-
|
|
42635
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
42636
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 mb-2 text-center", children: "Line Output" }),
|
|
42637
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42419
42638
|
OutputProgressChart,
|
|
42420
42639
|
{
|
|
42421
42640
|
currentOutput: lineInfo?.metrics.current_output || 0,
|
|
@@ -42423,9 +42642,9 @@ var MetricCards = React23.memo(({ lineInfo }) => {
|
|
|
42423
42642
|
}
|
|
42424
42643
|
) }) })
|
|
42425
42644
|
] }),
|
|
42426
|
-
/* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[
|
|
42645
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
42427
42646
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 text-center mb-2", children: "Underperforming Workspaces" }),
|
|
42428
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center h-[calc(100%-
|
|
42647
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: [
|
|
42429
42648
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-red-600", children: lineInfo?.metrics.underperforming_workspaces }),
|
|
42430
42649
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xl sm:text-2xl md:text-2xl lg:text-3xl text-gray-500 ml-1 sm:ml-2", children: [
|
|
42431
42650
|
"/ ",
|
|
@@ -42433,9 +42652,9 @@ var MetricCards = React23.memo(({ lineInfo }) => {
|
|
|
42433
42652
|
] })
|
|
42434
42653
|
] })
|
|
42435
42654
|
] }),
|
|
42436
|
-
/* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[
|
|
42655
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
42437
42656
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 text-center mb-2", children: "Average Efficiency" }),
|
|
42438
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-
|
|
42657
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: /* @__PURE__ */ jsxRuntime.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: [
|
|
42439
42658
|
lineInfo?.metrics.avg_efficiency.toFixed(1),
|
|
42440
42659
|
"%"
|
|
42441
42660
|
] }) })
|
|
@@ -51992,6 +52211,7 @@ exports.CardTitle = CardTitle2;
|
|
|
51992
52211
|
exports.ClipFilterProvider = ClipFilterProvider;
|
|
51993
52212
|
exports.CompactWorkspaceHealthCard = CompactWorkspaceHealthCard;
|
|
51994
52213
|
exports.CongratulationsOverlay = CongratulationsOverlay;
|
|
52214
|
+
exports.CroppedHlsVideoPlayer = CroppedHlsVideoPlayer;
|
|
51995
52215
|
exports.CroppedVideoPlayer = CroppedVideoPlayer;
|
|
51996
52216
|
exports.CycleTimeChart = CycleTimeChart;
|
|
51997
52217
|
exports.CycleTimeOverTimeChart = CycleTimeOverTimeChart;
|
|
@@ -52032,6 +52252,7 @@ exports.Header = Header;
|
|
|
52032
52252
|
exports.HealthStatusGrid = HealthStatusGrid;
|
|
52033
52253
|
exports.HealthStatusIndicator = HealthStatusIndicator;
|
|
52034
52254
|
exports.HelpView = HelpView_default;
|
|
52255
|
+
exports.HlsVideoPlayer = HlsVideoPlayer;
|
|
52035
52256
|
exports.HomeView = HomeView_default;
|
|
52036
52257
|
exports.HourlyOutputChart = HourlyOutputChart2;
|
|
52037
52258
|
exports.ISTTimer = ISTTimer_default;
|