@optifye/dashboard-core 6.9.7 → 6.9.9
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 -32
- package/dist/index.d.mts +58 -37
- package/dist/index.d.ts +58 -37
- package/dist/index.js +1095 -1020
- package/dist/index.mjs +1094 -1020
- 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);
|
|
@@ -9606,9 +9603,6 @@ var getAllWorkspaceDisplayNamesAsync = async (companyId, lineId) => {
|
|
|
9606
9603
|
Object.entries(runtimeWorkspaceDisplayNames).forEach(([lineId2, lineNames]) => {
|
|
9607
9604
|
Object.entries(lineNames).forEach(([workspaceId, displayName]) => {
|
|
9608
9605
|
allNames[`${lineId2}_${workspaceId}`] = displayName;
|
|
9609
|
-
if (!allNames[workspaceId]) {
|
|
9610
|
-
allNames[workspaceId] = displayName;
|
|
9611
|
-
}
|
|
9612
9606
|
});
|
|
9613
9607
|
});
|
|
9614
9608
|
return allNames;
|
|
@@ -14087,9 +14081,9 @@ var S3VideoPreloader = class {
|
|
|
14087
14081
|
this.processQueue();
|
|
14088
14082
|
};
|
|
14089
14083
|
if (url.endsWith(".m3u8")) {
|
|
14090
|
-
import('hls.js').then(({ default:
|
|
14091
|
-
if (
|
|
14092
|
-
const hls = new
|
|
14084
|
+
import('hls.js').then(({ default: Hls4 }) => {
|
|
14085
|
+
if (Hls4.isSupported()) {
|
|
14086
|
+
const hls = new Hls4({
|
|
14093
14087
|
maxBufferLength: 10,
|
|
14094
14088
|
startFragPrefetch: true,
|
|
14095
14089
|
lowLatencyMode: false,
|
|
@@ -14101,7 +14095,7 @@ var S3VideoPreloader = class {
|
|
|
14101
14095
|
hls.destroy();
|
|
14102
14096
|
cleanup();
|
|
14103
14097
|
}, 4e3);
|
|
14104
|
-
hls.on(
|
|
14098
|
+
hls.on(Hls4.Events.BUFFER_APPENDED, () => {
|
|
14105
14099
|
window.clearTimeout(timeout);
|
|
14106
14100
|
hls.destroy();
|
|
14107
14101
|
cleanup();
|
|
@@ -23105,7 +23099,7 @@ var OutputProgressChartComponent = ({
|
|
|
23105
23099
|
];
|
|
23106
23100
|
const COLORS = ["#00AB45", "#f3f4f6"];
|
|
23107
23101
|
const percentage = (currentOutput / targetOutput * 100).toFixed(1);
|
|
23108
|
-
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)" }, children: [
|
|
23109
23103
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.PieChart, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23110
23104
|
recharts.Pie,
|
|
23111
23105
|
{
|
|
@@ -24737,7 +24731,8 @@ var VideoGridView = React23__namespace.default.memo(({
|
|
|
24737
24731
|
displayName: (
|
|
24738
24732
|
// Create line-aware lookup key: lineId_workspaceName
|
|
24739
24733
|
// This ensures correct mapping when multiple lines have same workspace names
|
|
24740
|
-
displayNames[`${workspace.line_id}_${workspace.workspace_name}`] ||
|
|
24734
|
+
displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
|
|
24735
|
+
getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id)
|
|
24741
24736
|
),
|
|
24742
24737
|
useRAF: canvasConfig?.useRAF,
|
|
24743
24738
|
compact: !selectedLine,
|
|
@@ -24787,7 +24782,8 @@ var MapGridView = React23__namespace.default.memo(({
|
|
|
24787
24782
|
efficiency: workspace.efficiency,
|
|
24788
24783
|
action_count: workspace.action_count
|
|
24789
24784
|
});
|
|
24790
|
-
const displayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] ||
|
|
24785
|
+
const displayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
|
|
24786
|
+
getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
24791
24787
|
const navParams = getWorkspaceNavigationParams(workspaceId, displayName, workspace.line_id);
|
|
24792
24788
|
router$1.push(`/workspace/${workspaceId}${navParams}`);
|
|
24793
24789
|
}, [router$1, displayNames]);
|
|
@@ -24816,7 +24812,8 @@ var MapGridView = React23__namespace.default.memo(({
|
|
|
24816
24812
|
if (!workspace) return null;
|
|
24817
24813
|
const workspaceId = workspace.workspace_uuid || workspace.workspace_name;
|
|
24818
24814
|
getPerformanceColor(workspace.efficiency);
|
|
24819
|
-
const workspaceDisplayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] ||
|
|
24815
|
+
const workspaceDisplayName = displayNames[`${workspace.line_id}_${workspace.workspace_name}`] || // Always pass line_id to fallback to ensure correct mapping per line
|
|
24816
|
+
getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
24820
24817
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
24821
24818
|
motion.div,
|
|
24822
24819
|
{
|
|
@@ -26054,12 +26051,6 @@ var AxelNotificationPopup = ({
|
|
|
26054
26051
|
};
|
|
26055
26052
|
|
|
26056
26053
|
// 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
26054
|
var getSeverityColor = (severity) => {
|
|
26064
26055
|
switch (severity) {
|
|
26065
26056
|
case "low":
|
|
@@ -26072,473 +26063,6 @@ var getSeverityColor = (severity) => {
|
|
|
26072
26063
|
return "bg-gray-500";
|
|
26073
26064
|
}
|
|
26074
26065
|
};
|
|
26075
|
-
function Skeleton({ className, ...props }) {
|
|
26076
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("animate-pulse rounded-md bg-muted", className), ...props });
|
|
26077
|
-
}
|
|
26078
|
-
var Select = SelectPrimitive__namespace.Root;
|
|
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
|
|
26183
|
-
}) => {
|
|
26184
|
-
if (!isVisible) return null;
|
|
26185
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
26186
|
-
motion.div,
|
|
26187
|
-
{
|
|
26188
|
-
initial: { opacity: 0 },
|
|
26189
|
-
animate: { opacity: 1 },
|
|
26190
|
-
exit: { opacity: 0 },
|
|
26191
|
-
transition: { duration: 0.2 },
|
|
26192
|
-
className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
|
|
26193
|
-
"aria-modal": "true",
|
|
26194
|
-
role: "dialog",
|
|
26195
|
-
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 }) })
|
|
26196
|
-
}
|
|
26197
|
-
);
|
|
26198
|
-
};
|
|
26199
|
-
var LoadingOverlay_default = LoadingOverlay;
|
|
26200
|
-
var TimeDisplay = ({ className, variant = "default" }) => {
|
|
26201
|
-
const { dateTimeConfig } = useDashboardConfig();
|
|
26202
|
-
const [time2, setTime] = React23.useState("");
|
|
26203
|
-
const dbTimezone = useAppTimezone();
|
|
26204
|
-
const timezoneToDisplay = dbTimezone || dateTimeConfig?.defaultTimezone || "UTC";
|
|
26205
|
-
const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
|
|
26206
|
-
const timeSuffix = "";
|
|
26207
|
-
React23.useEffect(() => {
|
|
26208
|
-
const updateTime = () => {
|
|
26209
|
-
const now2 = /* @__PURE__ */ new Date();
|
|
26210
|
-
const effectiveFormatOptions = {
|
|
26211
|
-
hour: "2-digit",
|
|
26212
|
-
minute: "2-digit",
|
|
26213
|
-
second: "2-digit",
|
|
26214
|
-
hour12: true,
|
|
26215
|
-
timeZone: timezoneToDisplay,
|
|
26216
|
-
...dateTimeConfig?.timeFormatOptions || {}
|
|
26217
|
-
// Allow override from config
|
|
26218
|
-
};
|
|
26219
|
-
try {
|
|
26220
|
-
setTime(new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2));
|
|
26221
|
-
} catch (e) {
|
|
26222
|
-
console.error("Error formatting time:", e);
|
|
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
|
-
] });
|
|
26237
|
-
}
|
|
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
|
-
};
|
|
26256
|
-
var TimeDisplay_default = TimeDisplay;
|
|
26257
|
-
var DateDisplay = ({ className, variant = "default" }) => {
|
|
26258
|
-
const { dateTimeConfig } = useDashboardConfig();
|
|
26259
|
-
const [date, setDate] = React23.useState("");
|
|
26260
|
-
const timezoneToDisplay = dateTimeConfig?.defaultTimezone || "UTC";
|
|
26261
|
-
const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
|
|
26262
|
-
React23.useEffect(() => {
|
|
26263
|
-
const getCurrentFormattedDate = () => {
|
|
26264
|
-
const now2 = /* @__PURE__ */ new Date();
|
|
26265
|
-
const effectiveFormatOptions = {
|
|
26266
|
-
weekday: "short",
|
|
26267
|
-
day: "numeric",
|
|
26268
|
-
month: "short",
|
|
26269
|
-
timeZone: timezoneToDisplay,
|
|
26270
|
-
...dateTimeConfig?.dateFormatOptions || {}
|
|
26271
|
-
// Allow override from config
|
|
26272
|
-
};
|
|
26273
|
-
try {
|
|
26274
|
-
return new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2);
|
|
26275
|
-
} catch (e) {
|
|
26276
|
-
console.error("Error formatting date:", e);
|
|
26277
|
-
return "Error";
|
|
26278
|
-
}
|
|
26279
|
-
};
|
|
26280
|
-
const updateDate = () => {
|
|
26281
|
-
setDate(getCurrentFormattedDate());
|
|
26282
|
-
};
|
|
26283
|
-
updateDate();
|
|
26284
|
-
const interval = setInterval(() => {
|
|
26285
|
-
const currentDateStr = getCurrentFormattedDate();
|
|
26286
|
-
if (currentDateStr !== date) {
|
|
26287
|
-
updateDate();
|
|
26288
|
-
}
|
|
26289
|
-
}, 60 * 1e3);
|
|
26290
|
-
return () => clearInterval(interval);
|
|
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
|
-
]
|
|
26307
|
-
}
|
|
26308
|
-
);
|
|
26309
|
-
};
|
|
26310
|
-
var DateDisplay_default = DateDisplay;
|
|
26311
|
-
var Card3 = Card2;
|
|
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("");
|
|
26381
|
-
}
|
|
26382
|
-
};
|
|
26383
|
-
document.addEventListener("mousedown", handleClickOutside);
|
|
26384
|
-
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
26385
|
-
}, []);
|
|
26386
|
-
const handleKeyDown = (e) => {
|
|
26387
|
-
if (e.key === "Escape") {
|
|
26388
|
-
setIsOpen(false);
|
|
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";
|
|
26484
|
-
};
|
|
26485
|
-
this.state = {
|
|
26486
|
-
hasError: false,
|
|
26487
|
-
errorCount: 0,
|
|
26488
|
-
lastError: null,
|
|
26489
|
-
errorInfo: null
|
|
26490
|
-
};
|
|
26491
|
-
}
|
|
26492
|
-
static getDerivedStateFromError(error) {
|
|
26493
|
-
return { hasError: true };
|
|
26494
|
-
}
|
|
26495
|
-
componentDidCatch(error, errorInfo) {
|
|
26496
|
-
console.error("[ErrorBoundary] Caught render error:", {
|
|
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
26066
|
var PlayPauseIndicator = ({
|
|
26543
26067
|
show,
|
|
26544
26068
|
isPlaying,
|
|
@@ -26601,37 +26125,33 @@ var PlayPauseIndicator = ({
|
|
|
26601
26125
|
);
|
|
26602
26126
|
};
|
|
26603
26127
|
var ERROR_MAPPING = {
|
|
26604
|
-
|
|
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
|
|
26128
|
+
"networkError": {
|
|
26613
26129
|
code: 2,
|
|
26614
|
-
type: "recoverable"
|
|
26130
|
+
type: "recoverable",
|
|
26615
26131
|
message: "Network error - please check your internet connection",
|
|
26616
26132
|
canRetry: true
|
|
26617
26133
|
},
|
|
26618
|
-
|
|
26619
|
-
// MEDIA_ERR_DECODE
|
|
26134
|
+
"mediaError": {
|
|
26620
26135
|
code: 3,
|
|
26621
|
-
type: "non_recoverable"
|
|
26136
|
+
type: "non_recoverable",
|
|
26622
26137
|
message: "Stream corrupted due to internet connection",
|
|
26623
26138
|
canRetry: false
|
|
26624
26139
|
},
|
|
26625
|
-
|
|
26626
|
-
|
|
26140
|
+
"muxError": {
|
|
26141
|
+
code: 3,
|
|
26142
|
+
type: "non_recoverable",
|
|
26143
|
+
message: "Error processing media stream",
|
|
26144
|
+
canRetry: false
|
|
26145
|
+
},
|
|
26146
|
+
"otherError": {
|
|
26627
26147
|
code: 4,
|
|
26628
|
-
type: "non_recoverable"
|
|
26148
|
+
type: "non_recoverable",
|
|
26629
26149
|
message: "Video format not supported by your browser. Please use Google Chrome.",
|
|
26630
26150
|
canRetry: false
|
|
26631
26151
|
}
|
|
26632
26152
|
};
|
|
26633
|
-
var
|
|
26634
|
-
.video-player-container {
|
|
26153
|
+
var hlsVideoPlayerStyles = `
|
|
26154
|
+
.hls-video-player-container {
|
|
26635
26155
|
width: 100%;
|
|
26636
26156
|
height: 100%;
|
|
26637
26157
|
background-color: #000;
|
|
@@ -26639,32 +26159,18 @@ var videoPlayerStyles = `
|
|
|
26639
26159
|
align-items: center;
|
|
26640
26160
|
justify-content: center;
|
|
26641
26161
|
}
|
|
26642
|
-
|
|
26643
|
-
/* Center the video
|
|
26644
|
-
.video-
|
|
26162
|
+
|
|
26163
|
+
/* Center the video and maintain aspect ratio */
|
|
26164
|
+
.hls-video-element {
|
|
26645
26165
|
width: 100%;
|
|
26646
26166
|
height: 100%;
|
|
26647
26167
|
max-width: 100%;
|
|
26648
26168
|
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
26169
|
object-fit: contain;
|
|
26659
26170
|
}
|
|
26660
|
-
|
|
26661
|
-
/* Hide default Video.js loading spinner */
|
|
26662
|
-
.video-js .vjs-loading-spinner {
|
|
26663
|
-
display: none !important;
|
|
26664
|
-
}
|
|
26665
|
-
|
|
26171
|
+
|
|
26666
26172
|
/* Custom loading indicator styles */
|
|
26667
|
-
.video-player-loading {
|
|
26173
|
+
.hls-video-player-loading {
|
|
26668
26174
|
position: absolute;
|
|
26669
26175
|
top: 50%;
|
|
26670
26176
|
left: 50%;
|
|
@@ -26673,15 +26179,41 @@ var videoPlayerStyles = `
|
|
|
26673
26179
|
}
|
|
26674
26180
|
`;
|
|
26675
26181
|
if (typeof document !== "undefined") {
|
|
26676
|
-
const styleId = "video-player-custom-styles";
|
|
26182
|
+
const styleId = "hls-video-player-custom-styles";
|
|
26677
26183
|
if (!document.getElementById(styleId)) {
|
|
26678
26184
|
const style = document.createElement("style");
|
|
26679
26185
|
style.id = styleId;
|
|
26680
|
-
style.textContent =
|
|
26186
|
+
style.textContent = hlsVideoPlayerStyles;
|
|
26681
26187
|
document.head.appendChild(style);
|
|
26682
26188
|
}
|
|
26683
26189
|
}
|
|
26684
|
-
var
|
|
26190
|
+
var BASE_HLS_CONFIG = {
|
|
26191
|
+
maxBufferLength: 3,
|
|
26192
|
+
maxMaxBufferLength: 8,
|
|
26193
|
+
maxBufferSize: 50 * 1e3 * 1e3,
|
|
26194
|
+
maxBufferHole: 0.25,
|
|
26195
|
+
manifestLoadingTimeOut: 15e3,
|
|
26196
|
+
manifestLoadingMaxRetry: 3,
|
|
26197
|
+
manifestLoadingRetryDelay: 500,
|
|
26198
|
+
levelLoadingTimeOut: 6e4,
|
|
26199
|
+
levelLoadingMaxRetry: 5,
|
|
26200
|
+
levelLoadingRetryDelay: 500,
|
|
26201
|
+
fragLoadingTimeOut: 6e4,
|
|
26202
|
+
fragLoadingMaxRetry: 5,
|
|
26203
|
+
fragLoadingRetryDelay: 500,
|
|
26204
|
+
startPosition: -1,
|
|
26205
|
+
debug: false,
|
|
26206
|
+
enableWorker: true,
|
|
26207
|
+
lowLatencyMode: false,
|
|
26208
|
+
progressive: true,
|
|
26209
|
+
abrEwmaSlowLive: 9,
|
|
26210
|
+
abrEwmaFastLive: 3,
|
|
26211
|
+
abrBandWidthFactor: 0.95,
|
|
26212
|
+
abrBandWidthUpFactor: 0.7,
|
|
26213
|
+
abrMaxWithRealBitrate: false,
|
|
26214
|
+
abrEwmaDefaultEstimate: 5e7
|
|
26215
|
+
};
|
|
26216
|
+
var HlsVideoPlayer = React23.forwardRef(({
|
|
26685
26217
|
src,
|
|
26686
26218
|
poster,
|
|
26687
26219
|
autoplay = false,
|
|
@@ -26690,7 +26222,9 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
26690
26222
|
muted = false,
|
|
26691
26223
|
playsInline = true,
|
|
26692
26224
|
className = "",
|
|
26225
|
+
hlsConfig = {},
|
|
26693
26226
|
options = {},
|
|
26227
|
+
// Backward compatibility with Video.js
|
|
26694
26228
|
externalLoadingControl = false,
|
|
26695
26229
|
onLoadingChange,
|
|
26696
26230
|
onReady,
|
|
@@ -26708,321 +26242,396 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
26708
26242
|
onSeeked,
|
|
26709
26243
|
onClick
|
|
26710
26244
|
}, ref) => {
|
|
26245
|
+
const videoContainerRef = React23.useRef(null);
|
|
26711
26246
|
const videoRef = React23.useRef(null);
|
|
26712
|
-
const
|
|
26247
|
+
const hlsRef = React23.useRef(null);
|
|
26248
|
+
const blobUrlRef = React23.useRef(null);
|
|
26713
26249
|
const [isReady, setIsReady] = React23.useState(false);
|
|
26714
26250
|
const [isLoading, setIsLoading] = React23.useState(true);
|
|
26715
26251
|
const [showIndicator, setShowIndicator] = React23.useState(false);
|
|
26716
26252
|
const [indicatorIsPlaying, setIndicatorIsPlaying] = React23.useState(false);
|
|
26717
26253
|
const indicatorKeyRef = React23.useRef(0);
|
|
26718
|
-
const
|
|
26719
|
-
|
|
26720
|
-
|
|
26721
|
-
|
|
26722
|
-
|
|
26723
|
-
|
|
26724
|
-
|
|
26725
|
-
|
|
26726
|
-
|
|
26727
|
-
|
|
26728
|
-
|
|
26729
|
-
|
|
26730
|
-
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
|
|
26734
|
-
|
|
26735
|
-
|
|
26736
|
-
|
|
26737
|
-
|
|
26738
|
-
|
|
26739
|
-
|
|
26740
|
-
|
|
26741
|
-
|
|
26742
|
-
|
|
26743
|
-
|
|
26744
|
-
|
|
26745
|
-
|
|
26746
|
-
|
|
26747
|
-
|
|
26748
|
-
|
|
26749
|
-
|
|
26750
|
-
|
|
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", () => {
|
|
26846
|
-
setIsLoading(true);
|
|
26847
|
-
onLoadingChange?.(true);
|
|
26848
|
-
onLoadStart?.(player);
|
|
26849
|
-
});
|
|
26850
|
-
player.on("loadeddata", () => {
|
|
26851
|
-
setIsLoading(false);
|
|
26852
|
-
onLoadingChange?.(false);
|
|
26853
|
-
onLoadedData?.(player);
|
|
26854
|
-
});
|
|
26855
|
-
player.on("waiting", () => {
|
|
26856
|
-
setIsLoading(true);
|
|
26857
|
-
onLoadingChange?.(true);
|
|
26858
|
-
});
|
|
26859
|
-
player.on("playing", () => {
|
|
26860
|
-
setIsLoading(false);
|
|
26861
|
-
onLoadingChange?.(false);
|
|
26862
|
-
});
|
|
26863
|
-
player.on("loadedmetadata", () => {
|
|
26864
|
-
onLoadedMetadata?.(player);
|
|
26865
|
-
});
|
|
26866
|
-
player.on("seeking", () => onSeeking?.(player));
|
|
26867
|
-
player.on("seeked", () => onSeeked?.(player));
|
|
26868
|
-
player.on("error", () => {
|
|
26869
|
-
const error = player.error();
|
|
26870
|
-
const errorCode = error?.code ?? 0;
|
|
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
|
-
}
|
|
26912
|
-
}
|
|
26913
|
-
player.src({
|
|
26914
|
-
src: videoSrc,
|
|
26915
|
-
type: isHLS ? "application/x-mpegURL" : "video/mp4"
|
|
26916
|
-
});
|
|
26917
|
-
}
|
|
26254
|
+
const eventCallbacksRef = React23.useRef({
|
|
26255
|
+
onReady,
|
|
26256
|
+
onPlay,
|
|
26257
|
+
onPause,
|
|
26258
|
+
onPlaying,
|
|
26259
|
+
onTimeUpdate,
|
|
26260
|
+
onDurationChange,
|
|
26261
|
+
onEnded,
|
|
26262
|
+
onError,
|
|
26263
|
+
onLoadStart,
|
|
26264
|
+
onLoadedMetadata,
|
|
26265
|
+
onLoadedData,
|
|
26266
|
+
onSeeking,
|
|
26267
|
+
onSeeked,
|
|
26268
|
+
onLoadingChange
|
|
26269
|
+
});
|
|
26270
|
+
React23.useEffect(() => {
|
|
26271
|
+
eventCallbacksRef.current = {
|
|
26272
|
+
onReady,
|
|
26273
|
+
onPlay,
|
|
26274
|
+
onPause,
|
|
26275
|
+
onPlaying,
|
|
26276
|
+
onTimeUpdate,
|
|
26277
|
+
onDurationChange,
|
|
26278
|
+
onEnded,
|
|
26279
|
+
onError,
|
|
26280
|
+
onLoadStart,
|
|
26281
|
+
onLoadedMetadata,
|
|
26282
|
+
onLoadedData,
|
|
26283
|
+
onSeeking,
|
|
26284
|
+
onSeeked,
|
|
26285
|
+
onLoadingChange
|
|
26286
|
+
};
|
|
26918
26287
|
}, [
|
|
26919
|
-
src,
|
|
26920
|
-
defaultOptions,
|
|
26921
26288
|
onReady,
|
|
26922
26289
|
onPlay,
|
|
26923
26290
|
onPause,
|
|
26291
|
+
onPlaying,
|
|
26924
26292
|
onTimeUpdate,
|
|
26925
26293
|
onDurationChange,
|
|
26926
26294
|
onEnded,
|
|
26927
26295
|
onError,
|
|
26928
26296
|
onLoadStart,
|
|
26929
26297
|
onLoadedMetadata,
|
|
26298
|
+
onLoadedData,
|
|
26930
26299
|
onSeeking,
|
|
26931
|
-
onSeeked
|
|
26300
|
+
onSeeked,
|
|
26301
|
+
onLoadingChange
|
|
26932
26302
|
]);
|
|
26303
|
+
const stableHlsConfigRef = React23.useRef(hlsConfig);
|
|
26304
|
+
const stableOptionsRef = React23.useRef(options);
|
|
26305
|
+
const configSignatureRef = React23.useRef("");
|
|
26306
|
+
const [configVersion, setConfigVersion] = React23.useState(0);
|
|
26933
26307
|
React23.useEffect(() => {
|
|
26934
|
-
|
|
26935
|
-
|
|
26936
|
-
|
|
26937
|
-
|
|
26308
|
+
const serialized = JSON.stringify({
|
|
26309
|
+
hlsConfig: hlsConfig || null,
|
|
26310
|
+
options: options || null
|
|
26311
|
+
});
|
|
26312
|
+
if (!configSignatureRef.current) {
|
|
26313
|
+
configSignatureRef.current = serialized;
|
|
26314
|
+
stableHlsConfigRef.current = hlsConfig;
|
|
26315
|
+
stableOptionsRef.current = options;
|
|
26316
|
+
return;
|
|
26317
|
+
}
|
|
26318
|
+
if (configSignatureRef.current !== serialized) {
|
|
26319
|
+
configSignatureRef.current = serialized;
|
|
26320
|
+
stableHlsConfigRef.current = hlsConfig;
|
|
26321
|
+
stableOptionsRef.current = options;
|
|
26322
|
+
setConfigVersion((prev) => prev + 1);
|
|
26323
|
+
}
|
|
26324
|
+
}, [hlsConfig, options]);
|
|
26325
|
+
const cleanupBlobUrl = React23.useCallback(() => {
|
|
26326
|
+
if (blobUrlRef.current) {
|
|
26327
|
+
URL.revokeObjectURL(blobUrlRef.current);
|
|
26328
|
+
blobUrlRef.current = null;
|
|
26329
|
+
}
|
|
26330
|
+
}, []);
|
|
26331
|
+
const dispose = React23.useCallback(() => {
|
|
26332
|
+
if (hlsRef.current) {
|
|
26333
|
+
hlsRef.current.destroy();
|
|
26334
|
+
hlsRef.current = null;
|
|
26335
|
+
}
|
|
26336
|
+
cleanupBlobUrl();
|
|
26337
|
+
setIsReady(false);
|
|
26338
|
+
}, [cleanupBlobUrl]);
|
|
26339
|
+
const playerLikeObject = React23.useCallback(() => {
|
|
26340
|
+
return {
|
|
26341
|
+
el: () => videoRef.current,
|
|
26342
|
+
currentTime: () => videoRef.current?.currentTime || 0,
|
|
26343
|
+
duration: () => videoRef.current?.duration || 0,
|
|
26344
|
+
paused: () => videoRef.current?.paused ?? true,
|
|
26345
|
+
play: () => videoRef.current?.play(),
|
|
26346
|
+
pause: () => videoRef.current?.pause(),
|
|
26347
|
+
muted: (val) => {
|
|
26348
|
+
if (videoRef.current) {
|
|
26349
|
+
if (val !== void 0) videoRef.current.muted = val;
|
|
26350
|
+
return videoRef.current.muted;
|
|
26351
|
+
}
|
|
26352
|
+
return false;
|
|
26353
|
+
},
|
|
26354
|
+
volume: (val) => {
|
|
26355
|
+
if (videoRef.current) {
|
|
26356
|
+
if (val !== void 0) videoRef.current.volume = val;
|
|
26357
|
+
return videoRef.current.volume;
|
|
26358
|
+
}
|
|
26359
|
+
return 1;
|
|
26360
|
+
},
|
|
26361
|
+
error: () => null,
|
|
26362
|
+
dispose: () => dispose()
|
|
26363
|
+
};
|
|
26364
|
+
}, [dispose]);
|
|
26365
|
+
const initializePlayer = React23.useCallback(() => {
|
|
26366
|
+
if (!videoRef.current || !src) return;
|
|
26367
|
+
const video = videoRef.current;
|
|
26368
|
+
const player = playerLikeObject();
|
|
26369
|
+
const mergedHlsConfig = {
|
|
26370
|
+
...BASE_HLS_CONFIG,
|
|
26371
|
+
...stableHlsConfigRef.current || {},
|
|
26372
|
+
...stableOptionsRef.current || {}
|
|
26373
|
+
};
|
|
26374
|
+
cleanupBlobUrl();
|
|
26375
|
+
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
26376
|
+
if (isHLS) {
|
|
26938
26377
|
if (src.startsWith("#EXTM3U")) {
|
|
26939
26378
|
const safariMode = isSafari();
|
|
26940
26379
|
const browserName = getBrowserName();
|
|
26941
26380
|
if (safariMode) {
|
|
26942
26381
|
const clipIdMatch = src.match(/# Clip ID: ([a-f0-9-]+)/i);
|
|
26943
26382
|
if (clipIdMatch) {
|
|
26944
|
-
|
|
26945
|
-
console.log(`[
|
|
26383
|
+
const proxyUrl = `/api/clips/playlist/${clipIdMatch[1]}`;
|
|
26384
|
+
console.log(`[HlsVideoPlayer] Safari detected (${browserName}) - using playlist proxy URL:`, proxyUrl);
|
|
26385
|
+
video.src = proxyUrl;
|
|
26946
26386
|
} else {
|
|
26947
|
-
console.warn("[
|
|
26387
|
+
console.warn("[HlsVideoPlayer] Safari detected but no clip ID found in playlist, trying blob URL fallback");
|
|
26948
26388
|
const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
|
|
26949
|
-
blobUrl = URL.createObjectURL(blob);
|
|
26950
|
-
|
|
26389
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
26390
|
+
blobUrlRef.current = blobUrl;
|
|
26391
|
+
video.src = blobUrl;
|
|
26951
26392
|
}
|
|
26952
|
-
} else {
|
|
26393
|
+
} else if (Hls3__default.default.isSupported()) {
|
|
26953
26394
|
const blob = new Blob([src], { type: "application/vnd.apple.mpegurl" });
|
|
26954
|
-
blobUrl = URL.createObjectURL(blob);
|
|
26955
|
-
|
|
26956
|
-
console.log(`[
|
|
26395
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
26396
|
+
blobUrlRef.current = blobUrl;
|
|
26397
|
+
console.log(`[HlsVideoPlayer] Non-Safari browser (${browserName}) - using HLS.js with blob URL`);
|
|
26398
|
+
const hls = new Hls3__default.default(mergedHlsConfig);
|
|
26399
|
+
hlsRef.current = hls;
|
|
26400
|
+
hls.on(Hls3.Events.MANIFEST_PARSED, () => {
|
|
26401
|
+
console.log("[HlsVideoPlayer] Manifest parsed, ready to play");
|
|
26402
|
+
setIsReady(true);
|
|
26403
|
+
eventCallbacksRef.current.onReady?.(player);
|
|
26404
|
+
});
|
|
26405
|
+
hls.on(Hls3.Events.ERROR, (event, data) => {
|
|
26406
|
+
console.error("[HlsVideoPlayer] HLS.js error:", data);
|
|
26407
|
+
if (data.fatal) {
|
|
26408
|
+
let errorInfo;
|
|
26409
|
+
switch (data.type) {
|
|
26410
|
+
case Hls3.ErrorTypes.NETWORK_ERROR:
|
|
26411
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
26412
|
+
console.log("[HlsVideoPlayer] Attempting to recover from network error");
|
|
26413
|
+
hls.startLoad();
|
|
26414
|
+
break;
|
|
26415
|
+
case Hls3.ErrorTypes.MEDIA_ERROR:
|
|
26416
|
+
errorInfo = ERROR_MAPPING.mediaError;
|
|
26417
|
+
console.log("[HlsVideoPlayer] Attempting to recover from media error");
|
|
26418
|
+
hls.recoverMediaError();
|
|
26419
|
+
break;
|
|
26420
|
+
case Hls3.ErrorTypes.MUX_ERROR:
|
|
26421
|
+
errorInfo = ERROR_MAPPING.muxError;
|
|
26422
|
+
break;
|
|
26423
|
+
default:
|
|
26424
|
+
errorInfo = ERROR_MAPPING.otherError;
|
|
26425
|
+
break;
|
|
26426
|
+
}
|
|
26427
|
+
errorInfo.details = data.details;
|
|
26428
|
+
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
26429
|
+
}
|
|
26430
|
+
});
|
|
26431
|
+
hls.on(Hls3.Events.FRAG_LOADING, () => {
|
|
26432
|
+
setIsLoading(true);
|
|
26433
|
+
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
26434
|
+
});
|
|
26435
|
+
hls.on(Hls3.Events.FRAG_LOADED, () => {
|
|
26436
|
+
setIsLoading(false);
|
|
26437
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26438
|
+
});
|
|
26439
|
+
hls.loadSource(blobUrl);
|
|
26440
|
+
hls.attachMedia(video);
|
|
26441
|
+
} else {
|
|
26442
|
+
console.error("[HlsVideoPlayer] HLS.js not supported and not Safari - cannot play HLS content");
|
|
26443
|
+
const errorInfo = ERROR_MAPPING.otherError;
|
|
26444
|
+
onError?.(player, errorInfo);
|
|
26957
26445
|
}
|
|
26958
|
-
}
|
|
26959
|
-
|
|
26960
|
-
|
|
26961
|
-
|
|
26962
|
-
|
|
26963
|
-
|
|
26964
|
-
|
|
26965
|
-
|
|
26446
|
+
} else {
|
|
26447
|
+
if (Hls3__default.default.isSupported() && !isSafari()) {
|
|
26448
|
+
const hls = new Hls3__default.default(mergedHlsConfig);
|
|
26449
|
+
hlsRef.current = hls;
|
|
26450
|
+
hls.on(Hls3.Events.MANIFEST_PARSED, () => {
|
|
26451
|
+
setIsReady(true);
|
|
26452
|
+
eventCallbacksRef.current.onReady?.(player);
|
|
26453
|
+
});
|
|
26454
|
+
hls.on(Hls3.Events.ERROR, (event, data) => {
|
|
26455
|
+
console.error("[HlsVideoPlayer] HLS.js error:", data);
|
|
26456
|
+
if (data.fatal) {
|
|
26457
|
+
let errorInfo;
|
|
26458
|
+
switch (data.type) {
|
|
26459
|
+
case Hls3.ErrorTypes.NETWORK_ERROR:
|
|
26460
|
+
errorInfo = ERROR_MAPPING.networkError;
|
|
26461
|
+
hls.startLoad();
|
|
26462
|
+
break;
|
|
26463
|
+
case Hls3.ErrorTypes.MEDIA_ERROR:
|
|
26464
|
+
errorInfo = ERROR_MAPPING.mediaError;
|
|
26465
|
+
hls.recoverMediaError();
|
|
26466
|
+
break;
|
|
26467
|
+
default:
|
|
26468
|
+
errorInfo = ERROR_MAPPING.otherError;
|
|
26469
|
+
break;
|
|
26470
|
+
}
|
|
26471
|
+
errorInfo.details = data.details;
|
|
26472
|
+
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
26473
|
+
}
|
|
26474
|
+
});
|
|
26475
|
+
hls.loadSource(src);
|
|
26476
|
+
hls.attachMedia(video);
|
|
26477
|
+
} else {
|
|
26478
|
+
video.src = src;
|
|
26966
26479
|
}
|
|
26967
|
-
}
|
|
26480
|
+
}
|
|
26481
|
+
} else {
|
|
26482
|
+
video.src = src;
|
|
26968
26483
|
}
|
|
26969
|
-
|
|
26484
|
+
const handleCanPlay = () => {
|
|
26485
|
+
if (!hlsRef.current) {
|
|
26486
|
+
setIsReady(true);
|
|
26487
|
+
onReady?.(player);
|
|
26488
|
+
}
|
|
26489
|
+
};
|
|
26490
|
+
const handlePlay = () => eventCallbacksRef.current.onPlay?.(player);
|
|
26491
|
+
const handlePause = () => eventCallbacksRef.current.onPause?.(player);
|
|
26492
|
+
const handlePlaying = () => {
|
|
26493
|
+
setIsLoading(false);
|
|
26494
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26495
|
+
eventCallbacksRef.current.onPlaying?.(player);
|
|
26496
|
+
};
|
|
26497
|
+
const handleTimeUpdate = () => {
|
|
26498
|
+
const currentTime2 = video.currentTime || 0;
|
|
26499
|
+
eventCallbacksRef.current.onTimeUpdate?.(player, currentTime2);
|
|
26500
|
+
};
|
|
26501
|
+
const handleDurationChange = () => {
|
|
26502
|
+
const duration2 = video.duration || 0;
|
|
26503
|
+
eventCallbacksRef.current.onDurationChange?.(player, duration2);
|
|
26504
|
+
};
|
|
26505
|
+
const handleEnded = () => eventCallbacksRef.current.onEnded?.(player);
|
|
26506
|
+
const handleLoadStart = () => {
|
|
26507
|
+
setIsLoading(true);
|
|
26508
|
+
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
26509
|
+
eventCallbacksRef.current.onLoadStart?.(player);
|
|
26510
|
+
};
|
|
26511
|
+
const handleLoadedMetadata = () => eventCallbacksRef.current.onLoadedMetadata?.(player);
|
|
26512
|
+
const handleLoadedData = () => {
|
|
26513
|
+
setIsLoading(false);
|
|
26514
|
+
eventCallbacksRef.current.onLoadingChange?.(false);
|
|
26515
|
+
eventCallbacksRef.current.onLoadedData?.(player);
|
|
26516
|
+
};
|
|
26517
|
+
const handleWaiting = () => {
|
|
26518
|
+
setIsLoading(true);
|
|
26519
|
+
eventCallbacksRef.current.onLoadingChange?.(true);
|
|
26520
|
+
};
|
|
26521
|
+
const handleSeeking = () => eventCallbacksRef.current.onSeeking?.(player);
|
|
26522
|
+
const handleSeeked = () => eventCallbacksRef.current.onSeeked?.(player);
|
|
26523
|
+
const handleError = () => {
|
|
26524
|
+
const error = video.error;
|
|
26525
|
+
if (error) {
|
|
26526
|
+
const errorInfo = {
|
|
26527
|
+
code: error.code,
|
|
26528
|
+
type: error.code <= 2 ? "recoverable" : "non_recoverable",
|
|
26529
|
+
message: error.message || "Unknown error occurred",
|
|
26530
|
+
canRetry: error.code <= 2
|
|
26531
|
+
};
|
|
26532
|
+
eventCallbacksRef.current.onError?.(player, errorInfo);
|
|
26533
|
+
}
|
|
26534
|
+
};
|
|
26535
|
+
video.addEventListener("canplay", handleCanPlay);
|
|
26536
|
+
video.addEventListener("play", handlePlay);
|
|
26537
|
+
video.addEventListener("pause", handlePause);
|
|
26538
|
+
video.addEventListener("playing", handlePlaying);
|
|
26539
|
+
video.addEventListener("timeupdate", handleTimeUpdate);
|
|
26540
|
+
video.addEventListener("durationchange", handleDurationChange);
|
|
26541
|
+
video.addEventListener("ended", handleEnded);
|
|
26542
|
+
video.addEventListener("loadstart", handleLoadStart);
|
|
26543
|
+
video.addEventListener("loadedmetadata", handleLoadedMetadata);
|
|
26544
|
+
video.addEventListener("loadeddata", handleLoadedData);
|
|
26545
|
+
video.addEventListener("waiting", handleWaiting);
|
|
26546
|
+
video.addEventListener("seeking", handleSeeking);
|
|
26547
|
+
video.addEventListener("seeked", handleSeeked);
|
|
26548
|
+
video.addEventListener("error", handleError);
|
|
26549
|
+
return () => {
|
|
26550
|
+
video.removeEventListener("canplay", handleCanPlay);
|
|
26551
|
+
video.removeEventListener("play", handlePlay);
|
|
26552
|
+
video.removeEventListener("pause", handlePause);
|
|
26553
|
+
video.removeEventListener("playing", handlePlaying);
|
|
26554
|
+
video.removeEventListener("timeupdate", handleTimeUpdate);
|
|
26555
|
+
video.removeEventListener("durationchange", handleDurationChange);
|
|
26556
|
+
video.removeEventListener("ended", handleEnded);
|
|
26557
|
+
video.removeEventListener("loadstart", handleLoadStart);
|
|
26558
|
+
video.removeEventListener("loadedmetadata", handleLoadedMetadata);
|
|
26559
|
+
video.removeEventListener("loadeddata", handleLoadedData);
|
|
26560
|
+
video.removeEventListener("waiting", handleWaiting);
|
|
26561
|
+
video.removeEventListener("seeking", handleSeeking);
|
|
26562
|
+
video.removeEventListener("seeked", handleSeeked);
|
|
26563
|
+
video.removeEventListener("error", handleError);
|
|
26564
|
+
};
|
|
26565
|
+
}, [
|
|
26566
|
+
src,
|
|
26567
|
+
cleanupBlobUrl,
|
|
26568
|
+
playerLikeObject,
|
|
26569
|
+
configVersion
|
|
26570
|
+
]);
|
|
26970
26571
|
React23.useEffect(() => {
|
|
26971
|
-
initializePlayer();
|
|
26572
|
+
const cleanup = initializePlayer();
|
|
26972
26573
|
return () => {
|
|
26973
|
-
|
|
26974
|
-
|
|
26975
|
-
|
|
26976
|
-
|
|
26977
|
-
}
|
|
26978
|
-
playerRef.current.dispose();
|
|
26979
|
-
playerRef.current = null;
|
|
26980
|
-
setIsReady(false);
|
|
26574
|
+
cleanup?.();
|
|
26575
|
+
if (hlsRef.current) {
|
|
26576
|
+
hlsRef.current.destroy();
|
|
26577
|
+
hlsRef.current = null;
|
|
26981
26578
|
}
|
|
26579
|
+
cleanupBlobUrl();
|
|
26580
|
+
setIsReady(false);
|
|
26982
26581
|
};
|
|
26983
|
-
}, []);
|
|
26582
|
+
}, [src, initializePlayer, cleanupBlobUrl]);
|
|
26583
|
+
React23.useEffect(() => {
|
|
26584
|
+
if (videoRef.current) {
|
|
26585
|
+
if (autoplay) {
|
|
26586
|
+
videoRef.current.play().catch((err) => {
|
|
26587
|
+
console.warn("[HlsVideoPlayer] Autoplay failed:", err);
|
|
26588
|
+
});
|
|
26589
|
+
}
|
|
26590
|
+
}
|
|
26591
|
+
}, [autoplay]);
|
|
26984
26592
|
const play = React23.useCallback(() => {
|
|
26985
|
-
return
|
|
26593
|
+
return videoRef.current?.play();
|
|
26986
26594
|
}, []);
|
|
26987
26595
|
const pause = React23.useCallback(() => {
|
|
26988
|
-
|
|
26596
|
+
videoRef.current?.pause();
|
|
26989
26597
|
}, []);
|
|
26990
26598
|
const currentTime = React23.useCallback((time2) => {
|
|
26991
|
-
if (time2 !== void 0) {
|
|
26992
|
-
|
|
26599
|
+
if (time2 !== void 0 && videoRef.current) {
|
|
26600
|
+
videoRef.current.currentTime = time2;
|
|
26993
26601
|
return time2;
|
|
26994
26602
|
}
|
|
26995
|
-
return
|
|
26603
|
+
return videoRef.current?.currentTime || 0;
|
|
26996
26604
|
}, []);
|
|
26997
26605
|
const duration = React23.useCallback(() => {
|
|
26998
|
-
return
|
|
26606
|
+
return videoRef.current?.duration || 0;
|
|
26999
26607
|
}, []);
|
|
27000
26608
|
const paused = React23.useCallback(() => {
|
|
27001
|
-
return
|
|
26609
|
+
return videoRef.current?.paused ?? true;
|
|
27002
26610
|
}, []);
|
|
27003
26611
|
const mute = React23.useCallback((isMuted) => {
|
|
27004
|
-
if (isMuted !== void 0) {
|
|
27005
|
-
|
|
26612
|
+
if (isMuted !== void 0 && videoRef.current) {
|
|
26613
|
+
videoRef.current.muted = isMuted;
|
|
27006
26614
|
return isMuted;
|
|
27007
26615
|
}
|
|
27008
|
-
return
|
|
26616
|
+
return videoRef.current?.muted ?? false;
|
|
27009
26617
|
}, []);
|
|
27010
26618
|
const volume = React23.useCallback((level) => {
|
|
27011
|
-
if (level !== void 0) {
|
|
27012
|
-
|
|
26619
|
+
if (level !== void 0 && videoRef.current) {
|
|
26620
|
+
videoRef.current.volume = level;
|
|
27013
26621
|
return level;
|
|
27014
26622
|
}
|
|
27015
|
-
return
|
|
26623
|
+
return videoRef.current?.volume ?? 1;
|
|
27016
26624
|
}, []);
|
|
27017
|
-
const
|
|
27018
|
-
if (
|
|
27019
|
-
|
|
27020
|
-
|
|
27021
|
-
setIsReady(false);
|
|
26625
|
+
const playbackRate = React23.useCallback((rate) => {
|
|
26626
|
+
if (rate !== void 0 && videoRef.current) {
|
|
26627
|
+
videoRef.current.playbackRate = rate;
|
|
26628
|
+
return rate;
|
|
27022
26629
|
}
|
|
26630
|
+
return videoRef.current?.playbackRate ?? 1;
|
|
27023
26631
|
}, []);
|
|
27024
|
-
|
|
27025
|
-
|
|
26632
|
+
React23.useImperativeHandle(ref, () => ({
|
|
26633
|
+
hls: hlsRef.current,
|
|
26634
|
+
video: videoRef.current,
|
|
27026
26635
|
play,
|
|
27027
26636
|
pause,
|
|
27028
26637
|
currentTime,
|
|
@@ -27030,13 +26639,15 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
27030
26639
|
paused,
|
|
27031
26640
|
mute,
|
|
27032
26641
|
volume,
|
|
26642
|
+
playbackRate,
|
|
27033
26643
|
dispose,
|
|
27034
|
-
isReady
|
|
27035
|
-
|
|
26644
|
+
isReady,
|
|
26645
|
+
// For backward compatibility with Video.js API
|
|
26646
|
+
player: playerLikeObject()
|
|
26647
|
+
}), [play, pause, currentTime, duration, paused, mute, volume, playbackRate, dispose, isReady, playerLikeObject]);
|
|
27036
26648
|
const handleClickWithIndicator = React23.useCallback(() => {
|
|
27037
|
-
if (!onClick || !
|
|
27038
|
-
const
|
|
27039
|
-
const willBePlaying = player.paused();
|
|
26649
|
+
if (!onClick || !videoRef.current) return;
|
|
26650
|
+
const willBePlaying = videoRef.current.paused;
|
|
27040
26651
|
setIndicatorIsPlaying(willBePlaying);
|
|
27041
26652
|
setShowIndicator(false);
|
|
27042
26653
|
setTimeout(() => {
|
|
@@ -27045,17 +26656,30 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
27045
26656
|
}, 0);
|
|
27046
26657
|
onClick();
|
|
27047
26658
|
}, [onClick]);
|
|
27048
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `video-player-wrapper ${className}`, style: { position: "relative", width: "100%", height: "100%" }, children: [
|
|
26659
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `hls-video-player-wrapper ${className}`, style: { position: "relative", width: "100%", height: "100%" }, children: [
|
|
27049
26660
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27050
26661
|
"div",
|
|
27051
26662
|
{
|
|
27052
|
-
className: "video-player-container",
|
|
27053
|
-
ref:
|
|
27054
|
-
|
|
26663
|
+
className: "hls-video-player-container",
|
|
26664
|
+
ref: videoContainerRef,
|
|
26665
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
26666
|
+
"video",
|
|
26667
|
+
{
|
|
26668
|
+
ref: videoRef,
|
|
26669
|
+
className: "hls-video-element",
|
|
26670
|
+
poster,
|
|
26671
|
+
controls,
|
|
26672
|
+
loop,
|
|
26673
|
+
muted,
|
|
26674
|
+
playsInline,
|
|
26675
|
+
autoPlay: autoplay,
|
|
26676
|
+
preload: "metadata"
|
|
26677
|
+
}
|
|
26678
|
+
)
|
|
27055
26679
|
}
|
|
27056
26680
|
),
|
|
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(
|
|
26681
|
+
isLoading && !externalLoadingControl && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hls-video-player-loading", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
26682
|
+
onClick && !controls && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27059
26683
|
"div",
|
|
27060
26684
|
{
|
|
27061
26685
|
onClick: handleClickWithIndicator,
|
|
@@ -27071,7 +26695,7 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
27071
26695
|
"aria-label": "Click to play/pause"
|
|
27072
26696
|
}
|
|
27073
26697
|
),
|
|
27074
|
-
onClick && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26698
|
+
onClick && !controls && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27075
26699
|
PlayPauseIndicator,
|
|
27076
26700
|
{
|
|
27077
26701
|
show: showIndicator,
|
|
@@ -27081,13 +26705,25 @@ var VideoPlayer = React23__namespace.default.forwardRef(({
|
|
|
27081
26705
|
)
|
|
27082
26706
|
] });
|
|
27083
26707
|
});
|
|
27084
|
-
|
|
27085
|
-
var
|
|
26708
|
+
HlsVideoPlayer.displayName = "HlsVideoPlayer";
|
|
26709
|
+
var VideoPlayer = HlsVideoPlayer;
|
|
26710
|
+
var CroppedHlsVideoPlayer = React23.forwardRef(({
|
|
27086
26711
|
crop,
|
|
27087
26712
|
debug = false,
|
|
27088
26713
|
onClick,
|
|
27089
26714
|
...videoProps
|
|
27090
26715
|
}, ref) => {
|
|
26716
|
+
const {
|
|
26717
|
+
onReady: onReadyProp,
|
|
26718
|
+
onPlay: onPlayProp,
|
|
26719
|
+
onPause: onPauseProp,
|
|
26720
|
+
onEnded: onEndedProp,
|
|
26721
|
+
onSeeking: onSeekingProp,
|
|
26722
|
+
onSeeked: onSeekedProp,
|
|
26723
|
+
onLoadedMetadata: onLoadedMetadataProp,
|
|
26724
|
+
className: inheritedClassName = ""
|
|
26725
|
+
} = videoProps;
|
|
26726
|
+
const videoSrc = videoProps.src;
|
|
27091
26727
|
const videoContainerRef = React23.useRef(null);
|
|
27092
26728
|
const hiddenVideoRef = React23.useRef(null);
|
|
27093
26729
|
const canvasRef = React23.useRef(null);
|
|
@@ -27106,8 +26742,11 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27106
26742
|
}
|
|
27107
26743
|
}, []);
|
|
27108
26744
|
React23.useImperativeHandle(ref, () => ({
|
|
27109
|
-
get
|
|
27110
|
-
return hiddenVideoRef.current?.
|
|
26745
|
+
get hls() {
|
|
26746
|
+
return hiddenVideoRef.current?.hls || null;
|
|
26747
|
+
},
|
|
26748
|
+
get video() {
|
|
26749
|
+
return hiddenVideoRef.current?.video || null;
|
|
27111
26750
|
},
|
|
27112
26751
|
play: () => hiddenVideoRef.current?.play() || void 0,
|
|
27113
26752
|
pause: () => hiddenVideoRef.current?.pause(),
|
|
@@ -27121,12 +26760,36 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27121
26760
|
paused: () => hiddenVideoRef.current?.paused() || true,
|
|
27122
26761
|
mute: (isMuted) => hiddenVideoRef.current?.mute(isMuted) || false,
|
|
27123
26762
|
volume: (level) => hiddenVideoRef.current?.volume(level) || 0,
|
|
26763
|
+
playbackRate: (rate) => hiddenVideoRef.current?.playbackRate(rate) || 1,
|
|
27124
26764
|
dispose: () => {
|
|
27125
26765
|
hiddenVideoRef.current?.dispose();
|
|
27126
26766
|
stopCanvasRendering();
|
|
27127
26767
|
},
|
|
27128
26768
|
get isReady() {
|
|
27129
26769
|
return hiddenVideoRef.current?.isReady || false;
|
|
26770
|
+
},
|
|
26771
|
+
// For backward compatibility with Video.js API
|
|
26772
|
+
get player() {
|
|
26773
|
+
const video = hiddenVideoRef.current?.video;
|
|
26774
|
+
if (!video) return null;
|
|
26775
|
+
return {
|
|
26776
|
+
el: () => video,
|
|
26777
|
+
currentTime: () => video.currentTime || 0,
|
|
26778
|
+
duration: () => video.duration || 0,
|
|
26779
|
+
paused: () => video.paused ?? true,
|
|
26780
|
+
play: () => video.play(),
|
|
26781
|
+
pause: () => video.pause(),
|
|
26782
|
+
muted: (val) => {
|
|
26783
|
+
if (val !== void 0) video.muted = val;
|
|
26784
|
+
return video.muted;
|
|
26785
|
+
},
|
|
26786
|
+
volume: (val) => {
|
|
26787
|
+
if (val !== void 0) video.volume = val;
|
|
26788
|
+
return video.volume;
|
|
26789
|
+
},
|
|
26790
|
+
error: () => null,
|
|
26791
|
+
dispose: () => hiddenVideoRef.current?.dispose()
|
|
26792
|
+
};
|
|
27130
26793
|
}
|
|
27131
26794
|
}), [stopCanvasRendering]);
|
|
27132
26795
|
const calculateCanvasDimensions = React23.useCallback(() => {
|
|
@@ -27186,24 +26849,24 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27186
26849
|
animationFrameRef.current = requestAnimationFrame(renderFrameToCanvas);
|
|
27187
26850
|
}, [crop]);
|
|
27188
26851
|
const handleVideoReady = React23.useCallback((player) => {
|
|
27189
|
-
console.log("[
|
|
27190
|
-
const videoEl =
|
|
26852
|
+
console.log("[CroppedHlsVideoPlayer] Video player ready");
|
|
26853
|
+
const videoEl = hiddenVideoRef.current?.video;
|
|
27191
26854
|
if (videoEl) {
|
|
27192
26855
|
videoElementRef.current = videoEl;
|
|
27193
26856
|
setIsVideoReady(true);
|
|
27194
26857
|
}
|
|
27195
|
-
|
|
27196
|
-
}, [
|
|
26858
|
+
onReadyProp?.(player);
|
|
26859
|
+
}, [onReadyProp]);
|
|
27197
26860
|
const handleVideoPlay = React23.useCallback((player) => {
|
|
27198
|
-
console.log("[
|
|
26861
|
+
console.log("[CroppedHlsVideoPlayer] Video playing, starting canvas rendering");
|
|
27199
26862
|
if (crop && canvasRef.current) {
|
|
27200
26863
|
setIsProcessing(true);
|
|
27201
26864
|
renderFrameToCanvas();
|
|
27202
26865
|
}
|
|
27203
|
-
|
|
27204
|
-
}, [crop, renderFrameToCanvas,
|
|
26866
|
+
onPlayProp?.(player);
|
|
26867
|
+
}, [crop, renderFrameToCanvas, onPlayProp]);
|
|
27205
26868
|
const handleVideoPause = React23.useCallback((player) => {
|
|
27206
|
-
console.log("[
|
|
26869
|
+
console.log("[CroppedHlsVideoPlayer] Video paused, stopping canvas rendering and CLEARING canvas");
|
|
27207
26870
|
stopCanvasRendering();
|
|
27208
26871
|
setIsProcessing(false);
|
|
27209
26872
|
if (canvasRef.current) {
|
|
@@ -27214,10 +26877,10 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27214
26877
|
ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
27215
26878
|
}
|
|
27216
26879
|
}
|
|
27217
|
-
|
|
27218
|
-
}, [stopCanvasRendering,
|
|
26880
|
+
onPauseProp?.(player);
|
|
26881
|
+
}, [stopCanvasRendering, onPauseProp]);
|
|
27219
26882
|
const handleVideoEnded = React23.useCallback((player) => {
|
|
27220
|
-
console.log("[
|
|
26883
|
+
console.log("[CroppedHlsVideoPlayer] Video ended, CLEARING canvas");
|
|
27221
26884
|
stopCanvasRendering();
|
|
27222
26885
|
setIsProcessing(false);
|
|
27223
26886
|
if (canvasRef.current) {
|
|
@@ -27228,27 +26891,27 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27228
26891
|
ctx.fillRect(0, 0, canvasRef.current.width, canvasRef.current.height);
|
|
27229
26892
|
}
|
|
27230
26893
|
}
|
|
27231
|
-
|
|
27232
|
-
}, [stopCanvasRendering,
|
|
26894
|
+
onEndedProp?.(player);
|
|
26895
|
+
}, [stopCanvasRendering, onEndedProp]);
|
|
27233
26896
|
const handleSeeking = React23.useCallback((player) => {
|
|
27234
|
-
console.log("[
|
|
27235
|
-
if (crop && !
|
|
26897
|
+
console.log("[CroppedHlsVideoPlayer] Video seeking");
|
|
26898
|
+
if (crop && !videoElementRef.current?.paused) {
|
|
27236
26899
|
renderFrameToCanvas();
|
|
27237
26900
|
}
|
|
27238
|
-
|
|
27239
|
-
}, [crop, renderFrameToCanvas,
|
|
26901
|
+
onSeekingProp?.(player);
|
|
26902
|
+
}, [crop, renderFrameToCanvas, onSeekingProp]);
|
|
27240
26903
|
const handleSeeked = React23.useCallback((player) => {
|
|
27241
|
-
console.log("[
|
|
27242
|
-
if (crop && !
|
|
26904
|
+
console.log("[CroppedHlsVideoPlayer] Video seeked");
|
|
26905
|
+
if (crop && !videoElementRef.current?.paused) {
|
|
27243
26906
|
renderFrameToCanvas();
|
|
27244
26907
|
}
|
|
27245
|
-
|
|
27246
|
-
}, [crop, renderFrameToCanvas,
|
|
26908
|
+
onSeekedProp?.(player);
|
|
26909
|
+
}, [crop, renderFrameToCanvas, onSeekedProp]);
|
|
27247
26910
|
const handleLoadedMetadata = React23.useCallback((player) => {
|
|
27248
|
-
console.log("[
|
|
26911
|
+
console.log("[CroppedHlsVideoPlayer] Video metadata loaded");
|
|
27249
26912
|
calculateCanvasDimensions();
|
|
27250
|
-
|
|
27251
|
-
}, [calculateCanvasDimensions,
|
|
26913
|
+
onLoadedMetadataProp?.(player);
|
|
26914
|
+
}, [calculateCanvasDimensions, onLoadedMetadataProp]);
|
|
27252
26915
|
React23.useEffect(() => {
|
|
27253
26916
|
calculateCanvasDimensions();
|
|
27254
26917
|
const handleResize = () => {
|
|
@@ -27264,25 +26927,25 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27264
26927
|
const canvas = canvasRef.current;
|
|
27265
26928
|
const ctx = canvas.getContext("2d");
|
|
27266
26929
|
if (ctx) {
|
|
27267
|
-
console.log("[
|
|
26930
|
+
console.log("[CroppedHlsVideoPlayer] Source changing - CLEARING CANVAS IMMEDIATELY");
|
|
27268
26931
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
27269
26932
|
ctx.fillStyle = "black";
|
|
27270
26933
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
27271
26934
|
}
|
|
27272
26935
|
}
|
|
27273
|
-
}, [
|
|
26936
|
+
}, [videoSrc, crop]);
|
|
27274
26937
|
React23.useEffect(() => {
|
|
27275
26938
|
return () => {
|
|
27276
26939
|
stopCanvasRendering();
|
|
27277
26940
|
};
|
|
27278
26941
|
}, [stopCanvasRendering]);
|
|
27279
26942
|
if (!crop) {
|
|
27280
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
26943
|
+
return /* @__PURE__ */ jsxRuntime.jsx(HlsVideoPlayer, { ref, ...videoProps, onClick });
|
|
27281
26944
|
}
|
|
27282
26945
|
const handleClickWithIndicator = () => {
|
|
27283
|
-
if (!onClick || !hiddenVideoRef.current?.
|
|
27284
|
-
const
|
|
27285
|
-
const willBePlaying =
|
|
26946
|
+
if (!onClick || !hiddenVideoRef.current?.video) return;
|
|
26947
|
+
const video = hiddenVideoRef.current.video;
|
|
26948
|
+
const willBePlaying = video.paused;
|
|
27286
26949
|
setIndicatorIsPlaying(willBePlaying);
|
|
27287
26950
|
setShowIndicator(false);
|
|
27288
26951
|
setTimeout(() => {
|
|
@@ -27295,11 +26958,11 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27295
26958
|
"div",
|
|
27296
26959
|
{
|
|
27297
26960
|
ref: videoContainerRef,
|
|
27298
|
-
className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""} ${
|
|
26961
|
+
className: `relative w-full h-full flex items-center justify-center bg-black ${onClick ? "cursor-pointer" : ""} ${inheritedClassName}`,
|
|
27299
26962
|
onClick: handleClickWithIndicator,
|
|
27300
26963
|
children: [
|
|
27301
26964
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
27302
|
-
|
|
26965
|
+
HlsVideoPlayer,
|
|
27303
26966
|
{
|
|
27304
26967
|
ref: hiddenVideoRef,
|
|
27305
26968
|
...videoProps,
|
|
@@ -27366,7 +27029,539 @@ var CroppedVideoPlayer = React23.forwardRef(({
|
|
|
27366
27029
|
}
|
|
27367
27030
|
);
|
|
27368
27031
|
});
|
|
27369
|
-
|
|
27032
|
+
CroppedHlsVideoPlayer.displayName = "CroppedHlsVideoPlayer";
|
|
27033
|
+
var CroppedVideoPlayer = CroppedHlsVideoPlayer;
|
|
27034
|
+
var getSupabaseClient2 = () => {
|
|
27035
|
+
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
|
27036
|
+
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
|
27037
|
+
if (!url || !key) {
|
|
27038
|
+
throw new Error("Supabase configuration missing");
|
|
27039
|
+
}
|
|
27040
|
+
return supabaseJs.createClient(url, key);
|
|
27041
|
+
};
|
|
27042
|
+
var getAuthToken3 = async () => {
|
|
27043
|
+
try {
|
|
27044
|
+
const supabase = getSupabaseClient2();
|
|
27045
|
+
const { data: { session } } = await supabase.auth.getSession();
|
|
27046
|
+
return session?.access_token || null;
|
|
27047
|
+
} catch (error) {
|
|
27048
|
+
console.error("[useWorkspaceCrop] Error getting auth token:", error);
|
|
27049
|
+
return null;
|
|
27050
|
+
}
|
|
27051
|
+
};
|
|
27052
|
+
function useWorkspaceCrop(workspaceId) {
|
|
27053
|
+
const [crop, setCrop] = React23.useState(null);
|
|
27054
|
+
const [isLoading, setIsLoading] = React23.useState(true);
|
|
27055
|
+
const [error, setError] = React23.useState(null);
|
|
27056
|
+
React23.useEffect(() => {
|
|
27057
|
+
if (!workspaceId) {
|
|
27058
|
+
setIsLoading(false);
|
|
27059
|
+
return;
|
|
27060
|
+
}
|
|
27061
|
+
const fetchCrop = async () => {
|
|
27062
|
+
setIsLoading(true);
|
|
27063
|
+
setError(null);
|
|
27064
|
+
try {
|
|
27065
|
+
const token = await getAuthToken3();
|
|
27066
|
+
if (!token) {
|
|
27067
|
+
throw new Error("Authentication required");
|
|
27068
|
+
}
|
|
27069
|
+
const response = await fetch("/api/clips/supabase", {
|
|
27070
|
+
method: "POST",
|
|
27071
|
+
headers: {
|
|
27072
|
+
"Content-Type": "application/json",
|
|
27073
|
+
"Authorization": `Bearer ${token}`
|
|
27074
|
+
},
|
|
27075
|
+
body: JSON.stringify({
|
|
27076
|
+
action: "crop",
|
|
27077
|
+
workspaceId
|
|
27078
|
+
})
|
|
27079
|
+
});
|
|
27080
|
+
if (!response.ok) {
|
|
27081
|
+
throw new Error(`Failed to fetch crop: ${response.statusText}`);
|
|
27082
|
+
}
|
|
27083
|
+
const data = await response.json();
|
|
27084
|
+
console.log(`[useWorkspaceCrop] Fetched crop for workspace ${workspaceId}:`, data.crop);
|
|
27085
|
+
setCrop(data.crop);
|
|
27086
|
+
} catch (err) {
|
|
27087
|
+
console.error("[useWorkspaceCrop] Error fetching crop:", err);
|
|
27088
|
+
setError(err instanceof Error ? err.message : "Failed to fetch crop configuration");
|
|
27089
|
+
setCrop(null);
|
|
27090
|
+
} finally {
|
|
27091
|
+
setIsLoading(false);
|
|
27092
|
+
}
|
|
27093
|
+
};
|
|
27094
|
+
fetchCrop();
|
|
27095
|
+
}, [workspaceId]);
|
|
27096
|
+
return { crop, isLoading, error };
|
|
27097
|
+
}
|
|
27098
|
+
function Skeleton({ className, ...props }) {
|
|
27099
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("animate-pulse rounded-md bg-muted", className), ...props });
|
|
27100
|
+
}
|
|
27101
|
+
var Select = SelectPrimitive__namespace.Root;
|
|
27102
|
+
var SelectGroup = SelectPrimitive__namespace.Group;
|
|
27103
|
+
var SelectValue = SelectPrimitive__namespace.Value;
|
|
27104
|
+
var SelectTrigger = React23__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27105
|
+
SelectPrimitive__namespace.Trigger,
|
|
27106
|
+
{
|
|
27107
|
+
ref,
|
|
27108
|
+
className: cn(
|
|
27109
|
+
"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",
|
|
27110
|
+
className
|
|
27111
|
+
),
|
|
27112
|
+
...props,
|
|
27113
|
+
children: [
|
|
27114
|
+
children,
|
|
27115
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
27116
|
+
]
|
|
27117
|
+
}
|
|
27118
|
+
));
|
|
27119
|
+
SelectTrigger.displayName = SelectPrimitive__namespace.Trigger.displayName;
|
|
27120
|
+
var SelectScrollUpButton = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27121
|
+
SelectPrimitive__namespace.ScrollUpButton,
|
|
27122
|
+
{
|
|
27123
|
+
ref,
|
|
27124
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
27125
|
+
...props,
|
|
27126
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4" })
|
|
27127
|
+
}
|
|
27128
|
+
));
|
|
27129
|
+
SelectScrollUpButton.displayName = SelectPrimitive__namespace.ScrollUpButton.displayName;
|
|
27130
|
+
var SelectScrollDownButton = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27131
|
+
SelectPrimitive__namespace.ScrollDownButton,
|
|
27132
|
+
{
|
|
27133
|
+
ref,
|
|
27134
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
27135
|
+
...props,
|
|
27136
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4" })
|
|
27137
|
+
}
|
|
27138
|
+
));
|
|
27139
|
+
SelectScrollDownButton.displayName = SelectPrimitive__namespace.ScrollDownButton.displayName;
|
|
27140
|
+
var SelectContent = React23__namespace.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27141
|
+
SelectPrimitive__namespace.Content,
|
|
27142
|
+
{
|
|
27143
|
+
ref,
|
|
27144
|
+
className: cn(
|
|
27145
|
+
"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]",
|
|
27146
|
+
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",
|
|
27147
|
+
className
|
|
27148
|
+
),
|
|
27149
|
+
position,
|
|
27150
|
+
...props,
|
|
27151
|
+
children: [
|
|
27152
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
|
|
27153
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27154
|
+
SelectPrimitive__namespace.Viewport,
|
|
27155
|
+
{
|
|
27156
|
+
className: cn(
|
|
27157
|
+
"p-1",
|
|
27158
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
27159
|
+
),
|
|
27160
|
+
children
|
|
27161
|
+
}
|
|
27162
|
+
),
|
|
27163
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
|
|
27164
|
+
]
|
|
27165
|
+
}
|
|
27166
|
+
) }));
|
|
27167
|
+
SelectContent.displayName = SelectPrimitive__namespace.Content.displayName;
|
|
27168
|
+
var SelectLabel = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27169
|
+
SelectPrimitive__namespace.Label,
|
|
27170
|
+
{
|
|
27171
|
+
ref,
|
|
27172
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", className),
|
|
27173
|
+
...props
|
|
27174
|
+
}
|
|
27175
|
+
));
|
|
27176
|
+
SelectLabel.displayName = SelectPrimitive__namespace.Label.displayName;
|
|
27177
|
+
var SelectItem = React23__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27178
|
+
SelectPrimitive__namespace.Item,
|
|
27179
|
+
{
|
|
27180
|
+
ref,
|
|
27181
|
+
className: cn(
|
|
27182
|
+
"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",
|
|
27183
|
+
className
|
|
27184
|
+
),
|
|
27185
|
+
...props,
|
|
27186
|
+
children: [
|
|
27187
|
+
/* @__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" }) }) }),
|
|
27188
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children })
|
|
27189
|
+
]
|
|
27190
|
+
}
|
|
27191
|
+
));
|
|
27192
|
+
SelectItem.displayName = SelectPrimitive__namespace.Item.displayName;
|
|
27193
|
+
var SelectSeparator = React23__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
27194
|
+
SelectPrimitive__namespace.Separator,
|
|
27195
|
+
{
|
|
27196
|
+
ref,
|
|
27197
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
27198
|
+
...props
|
|
27199
|
+
}
|
|
27200
|
+
));
|
|
27201
|
+
SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
|
|
27202
|
+
var LoadingOverlay = ({
|
|
27203
|
+
isVisible,
|
|
27204
|
+
message = "Loading...",
|
|
27205
|
+
className
|
|
27206
|
+
}) => {
|
|
27207
|
+
if (!isVisible) return null;
|
|
27208
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
27209
|
+
motion.div,
|
|
27210
|
+
{
|
|
27211
|
+
initial: { opacity: 0 },
|
|
27212
|
+
animate: { opacity: 1 },
|
|
27213
|
+
exit: { opacity: 0 },
|
|
27214
|
+
transition: { duration: 0.2 },
|
|
27215
|
+
className: `fixed inset-0 z-[100] flex items-center justify-center bg-black/30 backdrop-blur-sm ${className || ""}`,
|
|
27216
|
+
"aria-modal": "true",
|
|
27217
|
+
role: "dialog",
|
|
27218
|
+
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 }) })
|
|
27219
|
+
}
|
|
27220
|
+
);
|
|
27221
|
+
};
|
|
27222
|
+
var LoadingOverlay_default = LoadingOverlay;
|
|
27223
|
+
var TimeDisplay = ({ className, variant = "default" }) => {
|
|
27224
|
+
const { dateTimeConfig } = useDashboardConfig();
|
|
27225
|
+
const [time2, setTime] = React23.useState("");
|
|
27226
|
+
const dbTimezone = useAppTimezone();
|
|
27227
|
+
const timezoneToDisplay = dbTimezone || dateTimeConfig?.defaultTimezone || "UTC";
|
|
27228
|
+
const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
|
|
27229
|
+
const timeSuffix = "";
|
|
27230
|
+
React23.useEffect(() => {
|
|
27231
|
+
const updateTime = () => {
|
|
27232
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
27233
|
+
const effectiveFormatOptions = {
|
|
27234
|
+
hour: "2-digit",
|
|
27235
|
+
minute: "2-digit",
|
|
27236
|
+
second: "2-digit",
|
|
27237
|
+
hour12: true,
|
|
27238
|
+
timeZone: timezoneToDisplay,
|
|
27239
|
+
...dateTimeConfig?.timeFormatOptions || {}
|
|
27240
|
+
// Allow override from config
|
|
27241
|
+
};
|
|
27242
|
+
try {
|
|
27243
|
+
setTime(new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2));
|
|
27244
|
+
} catch (e) {
|
|
27245
|
+
console.error("Error formatting time:", e);
|
|
27246
|
+
setTime("Error");
|
|
27247
|
+
}
|
|
27248
|
+
};
|
|
27249
|
+
updateTime();
|
|
27250
|
+
const interval = setInterval(updateTime, 1e3);
|
|
27251
|
+
return () => clearInterval(interval);
|
|
27252
|
+
}, [timezoneToDisplay, dateTimeConfig?.timeFormatOptions, localeToUse]);
|
|
27253
|
+
if (!time2) return null;
|
|
27254
|
+
if (variant === "minimal") {
|
|
27255
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: className || "", children: [
|
|
27256
|
+
time2,
|
|
27257
|
+
" ",
|
|
27258
|
+
timeSuffix
|
|
27259
|
+
] });
|
|
27260
|
+
}
|
|
27261
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27262
|
+
motion.div,
|
|
27263
|
+
{
|
|
27264
|
+
initial: { opacity: 0, y: -5 },
|
|
27265
|
+
animate: { opacity: 1, y: 0 },
|
|
27266
|
+
transition: { duration: 0.3 },
|
|
27267
|
+
className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
|
|
27268
|
+
children: [
|
|
27269
|
+
/* @__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" }) }),
|
|
27270
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs sm:text-[11px] font-medium text-gray-800 tabular-nums tracking-tight", children: [
|
|
27271
|
+
time2,
|
|
27272
|
+
" ",
|
|
27273
|
+
timeSuffix
|
|
27274
|
+
] })
|
|
27275
|
+
]
|
|
27276
|
+
}
|
|
27277
|
+
);
|
|
27278
|
+
};
|
|
27279
|
+
var TimeDisplay_default = TimeDisplay;
|
|
27280
|
+
var DateDisplay = ({ className, variant = "default" }) => {
|
|
27281
|
+
const { dateTimeConfig } = useDashboardConfig();
|
|
27282
|
+
const [date, setDate] = React23.useState("");
|
|
27283
|
+
const timezoneToDisplay = dateTimeConfig?.defaultTimezone || "UTC";
|
|
27284
|
+
const localeToUse = dateTimeConfig?.defaultLocale || "en-US";
|
|
27285
|
+
React23.useEffect(() => {
|
|
27286
|
+
const getCurrentFormattedDate = () => {
|
|
27287
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
27288
|
+
const effectiveFormatOptions = {
|
|
27289
|
+
weekday: "short",
|
|
27290
|
+
day: "numeric",
|
|
27291
|
+
month: "short",
|
|
27292
|
+
timeZone: timezoneToDisplay,
|
|
27293
|
+
...dateTimeConfig?.dateFormatOptions || {}
|
|
27294
|
+
// Allow override from config
|
|
27295
|
+
};
|
|
27296
|
+
try {
|
|
27297
|
+
return new Intl.DateTimeFormat(localeToUse, effectiveFormatOptions).format(now2);
|
|
27298
|
+
} catch (e) {
|
|
27299
|
+
console.error("Error formatting date:", e);
|
|
27300
|
+
return "Error";
|
|
27301
|
+
}
|
|
27302
|
+
};
|
|
27303
|
+
const updateDate = () => {
|
|
27304
|
+
setDate(getCurrentFormattedDate());
|
|
27305
|
+
};
|
|
27306
|
+
updateDate();
|
|
27307
|
+
const interval = setInterval(() => {
|
|
27308
|
+
const currentDateStr = getCurrentFormattedDate();
|
|
27309
|
+
if (currentDateStr !== date) {
|
|
27310
|
+
updateDate();
|
|
27311
|
+
}
|
|
27312
|
+
}, 60 * 1e3);
|
|
27313
|
+
return () => clearInterval(interval);
|
|
27314
|
+
}, [date, timezoneToDisplay, dateTimeConfig?.dateFormatOptions, localeToUse]);
|
|
27315
|
+
if (!date) return null;
|
|
27316
|
+
if (variant === "minimal") {
|
|
27317
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: className || "", children: date });
|
|
27318
|
+
}
|
|
27319
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
27320
|
+
motion.div,
|
|
27321
|
+
{
|
|
27322
|
+
initial: { opacity: 0, y: -5 },
|
|
27323
|
+
animate: { opacity: 1, y: 0 },
|
|
27324
|
+
transition: { duration: 0.3 },
|
|
27325
|
+
className: `flex items-center space-x-1.5 bg-white/60 backdrop-blur-sm px-2 py-0.5 rounded-md shadow-xs ${className || ""}`,
|
|
27326
|
+
children: [
|
|
27327
|
+
/* @__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" }) }),
|
|
27328
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-gray-800 tracking-tight", children: date })
|
|
27329
|
+
]
|
|
27330
|
+
}
|
|
27331
|
+
);
|
|
27332
|
+
};
|
|
27333
|
+
var DateDisplay_default = DateDisplay;
|
|
27334
|
+
var Card3 = Card2;
|
|
27335
|
+
var CardHeader3 = CardHeader2;
|
|
27336
|
+
var CardTitle3 = CardTitle2;
|
|
27337
|
+
var CardContent3 = CardContent2;
|
|
27338
|
+
var MetricCard2 = ({ title, value, unit = "", trend = null }) => {
|
|
27339
|
+
const getTrendColor = (trendValue) => {
|
|
27340
|
+
if (trendValue === null || trendValue === void 0) return "";
|
|
27341
|
+
return trendValue > 0 ? "text-green-500" : trendValue < 0 ? "text-red-500" : "text-gray-500";
|
|
27342
|
+
};
|
|
27343
|
+
const getTrendSymbol = (trendValue) => {
|
|
27344
|
+
if (trendValue === null || trendValue === void 0) return "";
|
|
27345
|
+
return trendValue > 0 ? "\u2191" : trendValue < 0 ? "\u2193" : "";
|
|
27346
|
+
};
|
|
27347
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Card3, { className: "bg-white", children: [
|
|
27348
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHeader3, { className: "pb-2", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle3, { className: "text-sm font-medium text-gray-500", children: title }) }),
|
|
27349
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardContent3, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
27350
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-2xl font-semibold", children: [
|
|
27351
|
+
value,
|
|
27352
|
+
unit && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-sm text-gray-500", children: unit })
|
|
27353
|
+
] }),
|
|
27354
|
+
trend !== null && trend !== void 0 && // Check trend for null/undefined before accessing
|
|
27355
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center ${getTrendColor(trend)}`, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm", children: [
|
|
27356
|
+
getTrendSymbol(trend),
|
|
27357
|
+
" ",
|
|
27358
|
+
Math.abs(trend),
|
|
27359
|
+
"%"
|
|
27360
|
+
] }) })
|
|
27361
|
+
] }) })
|
|
27362
|
+
] });
|
|
27363
|
+
};
|
|
27364
|
+
var MetricCard_default = MetricCard2;
|
|
27365
|
+
var TimePickerDropdown = ({
|
|
27366
|
+
value,
|
|
27367
|
+
onChange,
|
|
27368
|
+
placeholder = "Select time",
|
|
27369
|
+
className = "",
|
|
27370
|
+
disabled = false
|
|
27371
|
+
}) => {
|
|
27372
|
+
const [isOpen, setIsOpen] = React23.useState(false);
|
|
27373
|
+
const [searchTerm, setSearchTerm] = React23.useState("");
|
|
27374
|
+
const dropdownRef = React23.useRef(null);
|
|
27375
|
+
const inputRef = React23.useRef(null);
|
|
27376
|
+
const generateTimeSlots = () => {
|
|
27377
|
+
const slots = [];
|
|
27378
|
+
for (let hour = 0; hour < 24; hour++) {
|
|
27379
|
+
for (let minute = 0; minute < 60; minute += 15) {
|
|
27380
|
+
const time24 = `${hour.toString().padStart(2, "0")}:${minute.toString().padStart(2, "0")}`;
|
|
27381
|
+
const hour12 = hour === 0 ? 12 : hour > 12 ? hour - 12 : hour;
|
|
27382
|
+
const ampm = hour < 12 ? "AM" : "PM";
|
|
27383
|
+
const time12 = `${hour12}:${minute.toString().padStart(2, "0")} ${ampm}`;
|
|
27384
|
+
slots.push({ value: time24, label: time12 });
|
|
27385
|
+
}
|
|
27386
|
+
}
|
|
27387
|
+
return slots;
|
|
27388
|
+
};
|
|
27389
|
+
const timeSlots = generateTimeSlots();
|
|
27390
|
+
const filteredSlots = timeSlots.filter(
|
|
27391
|
+
(slot) => slot.label.toLowerCase().includes(searchTerm.toLowerCase())
|
|
27392
|
+
);
|
|
27393
|
+
const getDisplayValue = (value2) => {
|
|
27394
|
+
if (!value2) return "";
|
|
27395
|
+
const normalizedValue = value2.substring(0, 5);
|
|
27396
|
+
const slot = timeSlots.find((s) => s.value === normalizedValue);
|
|
27397
|
+
return slot ? slot.label : value2;
|
|
27398
|
+
};
|
|
27399
|
+
React23.useEffect(() => {
|
|
27400
|
+
const handleClickOutside = (event) => {
|
|
27401
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
27402
|
+
setIsOpen(false);
|
|
27403
|
+
setSearchTerm("");
|
|
27404
|
+
}
|
|
27405
|
+
};
|
|
27406
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
27407
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
27408
|
+
}, []);
|
|
27409
|
+
const handleKeyDown = (e) => {
|
|
27410
|
+
if (e.key === "Escape") {
|
|
27411
|
+
setIsOpen(false);
|
|
27412
|
+
setSearchTerm("");
|
|
27413
|
+
} else if (e.key === "Enter") {
|
|
27414
|
+
e.preventDefault();
|
|
27415
|
+
if (filteredSlots.length > 0) {
|
|
27416
|
+
onChange(filteredSlots[0].value);
|
|
27417
|
+
setIsOpen(false);
|
|
27418
|
+
setSearchTerm("");
|
|
27419
|
+
}
|
|
27420
|
+
}
|
|
27421
|
+
};
|
|
27422
|
+
const handleSelect = (timeValue) => {
|
|
27423
|
+
onChange(timeValue);
|
|
27424
|
+
setIsOpen(false);
|
|
27425
|
+
setSearchTerm("");
|
|
27426
|
+
};
|
|
27427
|
+
const handleToggle = () => {
|
|
27428
|
+
if (disabled) return;
|
|
27429
|
+
setIsOpen(!isOpen);
|
|
27430
|
+
if (!isOpen) {
|
|
27431
|
+
setTimeout(() => inputRef.current?.focus(), 100);
|
|
27432
|
+
}
|
|
27433
|
+
};
|
|
27434
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `relative ${className}`, ref: dropdownRef, children: [
|
|
27435
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27436
|
+
"button",
|
|
27437
|
+
{
|
|
27438
|
+
type: "button",
|
|
27439
|
+
onClick: handleToggle,
|
|
27440
|
+
disabled,
|
|
27441
|
+
className: `
|
|
27442
|
+
w-full px-3 py-2 text-left bg-white border border-gray-300 rounded-md shadow-sm
|
|
27443
|
+
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500
|
|
27444
|
+
hover:border-gray-400 transition-colors duration-200
|
|
27445
|
+
${disabled ? "bg-gray-50 cursor-not-allowed" : "cursor-pointer"}
|
|
27446
|
+
${isOpen ? "ring-2 ring-blue-500 border-blue-500" : ""}
|
|
27447
|
+
`,
|
|
27448
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
27449
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
27450
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-4 w-4 text-gray-400" }),
|
|
27451
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-sm ${value ? "text-gray-900" : "text-gray-500"}`, children: value ? getDisplayValue(value) : placeholder })
|
|
27452
|
+
] }),
|
|
27453
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27454
|
+
lucideReact.ChevronDown,
|
|
27455
|
+
{
|
|
27456
|
+
className: `h-4 w-4 text-gray-400 transition-transform duration-200 ${isOpen ? "rotate-180" : ""}`
|
|
27457
|
+
}
|
|
27458
|
+
)
|
|
27459
|
+
] })
|
|
27460
|
+
}
|
|
27461
|
+
),
|
|
27462
|
+
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: [
|
|
27463
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-2 border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
27464
|
+
"input",
|
|
27465
|
+
{
|
|
27466
|
+
ref: inputRef,
|
|
27467
|
+
type: "text",
|
|
27468
|
+
placeholder: "Search time...",
|
|
27469
|
+
value: searchTerm,
|
|
27470
|
+
onChange: (e) => setSearchTerm(e.target.value),
|
|
27471
|
+
onKeyDown: handleKeyDown,
|
|
27472
|
+
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"
|
|
27473
|
+
}
|
|
27474
|
+
) }),
|
|
27475
|
+
/* @__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(
|
|
27476
|
+
"button",
|
|
27477
|
+
{
|
|
27478
|
+
type: "button",
|
|
27479
|
+
onClick: () => handleSelect(slot.value),
|
|
27480
|
+
className: `
|
|
27481
|
+
w-full px-3 py-2 text-left text-sm hover:bg-blue-50 hover:text-blue-600
|
|
27482
|
+
transition-colors duration-150 border-b border-gray-100 last:border-b-0
|
|
27483
|
+
${slot.value === value ? "bg-blue-50 text-blue-600 font-medium" : "text-gray-700"}
|
|
27484
|
+
`,
|
|
27485
|
+
children: slot.label
|
|
27486
|
+
},
|
|
27487
|
+
slot.value
|
|
27488
|
+
)) })
|
|
27489
|
+
] })
|
|
27490
|
+
] });
|
|
27491
|
+
};
|
|
27492
|
+
var SilentErrorBoundary = class extends React23__namespace.default.Component {
|
|
27493
|
+
constructor(props) {
|
|
27494
|
+
super(props);
|
|
27495
|
+
this.handleClearAndReload = () => {
|
|
27496
|
+
console.log("[ErrorBoundary] User initiated reset");
|
|
27497
|
+
if (typeof window !== "undefined") {
|
|
27498
|
+
try {
|
|
27499
|
+
localStorage.clear();
|
|
27500
|
+
sessionStorage.clear();
|
|
27501
|
+
console.log("[ErrorBoundary] Cleared all storage");
|
|
27502
|
+
} catch (error) {
|
|
27503
|
+
console.error("[ErrorBoundary] Failed to clear storage:", error);
|
|
27504
|
+
}
|
|
27505
|
+
}
|
|
27506
|
+
window.location.href = "/login";
|
|
27507
|
+
};
|
|
27508
|
+
this.state = {
|
|
27509
|
+
hasError: false,
|
|
27510
|
+
errorCount: 0,
|
|
27511
|
+
lastError: null,
|
|
27512
|
+
errorInfo: null
|
|
27513
|
+
};
|
|
27514
|
+
}
|
|
27515
|
+
static getDerivedStateFromError(error) {
|
|
27516
|
+
return { hasError: true };
|
|
27517
|
+
}
|
|
27518
|
+
componentDidCatch(error, errorInfo) {
|
|
27519
|
+
console.error("[ErrorBoundary] Caught render error:", {
|
|
27520
|
+
error: error.message,
|
|
27521
|
+
stack: error.stack,
|
|
27522
|
+
componentStack: errorInfo.componentStack,
|
|
27523
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
27524
|
+
});
|
|
27525
|
+
this.setState((prev) => ({
|
|
27526
|
+
errorCount: prev.errorCount + 1,
|
|
27527
|
+
lastError: error,
|
|
27528
|
+
errorInfo
|
|
27529
|
+
}));
|
|
27530
|
+
try {
|
|
27531
|
+
if (typeof window !== "undefined" && window.mixpanel) {
|
|
27532
|
+
window.mixpanel.track("React Render Error", {
|
|
27533
|
+
error: error.message,
|
|
27534
|
+
component: errorInfo.componentStack?.split("\n")[1] || "unknown",
|
|
27535
|
+
errorCount: this.state.errorCount + 1
|
|
27536
|
+
});
|
|
27537
|
+
}
|
|
27538
|
+
} catch (analyticsError) {
|
|
27539
|
+
console.warn("[ErrorBoundary] Analytics tracking failed:", analyticsError);
|
|
27540
|
+
}
|
|
27541
|
+
}
|
|
27542
|
+
render() {
|
|
27543
|
+
if (!this.state.hasError) {
|
|
27544
|
+
return this.props.children;
|
|
27545
|
+
}
|
|
27546
|
+
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: [
|
|
27547
|
+
/* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading Dashboard..." }),
|
|
27548
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-500", children: "Taking longer than usual..." }),
|
|
27549
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
27550
|
+
"a",
|
|
27551
|
+
{
|
|
27552
|
+
href: "#",
|
|
27553
|
+
onClick: (e) => {
|
|
27554
|
+
e.preventDefault();
|
|
27555
|
+
this.handleClearAndReload();
|
|
27556
|
+
},
|
|
27557
|
+
className: "text-xs text-gray-400 hover:text-gray-600 underline transition-colors",
|
|
27558
|
+
children: "Reset and try again"
|
|
27559
|
+
}
|
|
27560
|
+
) }),
|
|
27561
|
+
process.env.NODE_ENV === "development" && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-400 italic mt-4", children: "Check console for error details" })
|
|
27562
|
+
] }) });
|
|
27563
|
+
}
|
|
27564
|
+
};
|
|
27370
27565
|
var BackButton = ({
|
|
27371
27566
|
onClick,
|
|
27372
27567
|
text = "Back",
|
|
@@ -27731,70 +27926,6 @@ var NewClipsNotification = ({
|
|
|
27731
27926
|
}
|
|
27732
27927
|
);
|
|
27733
27928
|
};
|
|
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
27929
|
var parseCycleTime = (value) => {
|
|
27799
27930
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
27800
27931
|
return value;
|
|
@@ -29050,6 +29181,11 @@ var BottlenecksContent = ({
|
|
|
29050
29181
|
return Number.isFinite(numericValue) ? numericValue : null;
|
|
29051
29182
|
})();
|
|
29052
29183
|
const videoRef = React23.useRef(null);
|
|
29184
|
+
const videoPlayerOptions = React23.useMemo(() => ({
|
|
29185
|
+
fluid: false,
|
|
29186
|
+
responsive: false,
|
|
29187
|
+
fill: false
|
|
29188
|
+
}), []);
|
|
29053
29189
|
const [initialFilter, setInitialFilter] = React23.useState("");
|
|
29054
29190
|
const currentIndexRef = React23.useRef(0);
|
|
29055
29191
|
const activeFilterRef = React23.useRef(initialFilter);
|
|
@@ -29060,6 +29196,7 @@ var BottlenecksContent = ({
|
|
|
29060
29196
|
const [duration, setDuration] = React23.useState(0);
|
|
29061
29197
|
const [currentIndex, setCurrentIndex] = React23.useState(0);
|
|
29062
29198
|
const [currentClipId, setCurrentClipId] = React23.useState(null);
|
|
29199
|
+
const [playbackSpeed, setPlaybackSpeed] = React23.useState(1);
|
|
29063
29200
|
const [isTransitioning, setIsTransitioning] = React23.useState(false);
|
|
29064
29201
|
const [pendingVideo, setPendingVideo] = React23.useState(null);
|
|
29065
29202
|
const [isVideoBuffering, setIsVideoBuffering] = React23.useState(false);
|
|
@@ -29803,7 +29940,10 @@ var BottlenecksContent = ({
|
|
|
29803
29940
|
if (error?.isRetrying) {
|
|
29804
29941
|
setError(null);
|
|
29805
29942
|
}
|
|
29806
|
-
|
|
29943
|
+
if (videoRef.current?.playbackRate && playbackSpeed !== 1) {
|
|
29944
|
+
videoRef.current.playbackRate(playbackSpeed);
|
|
29945
|
+
}
|
|
29946
|
+
}, [error, playbackSpeed]);
|
|
29807
29947
|
const handleVideoPlay = React23.useCallback(async (player) => {
|
|
29808
29948
|
setIsPlaying(true);
|
|
29809
29949
|
setIsInitialLoading(false);
|
|
@@ -29973,7 +30113,13 @@ var BottlenecksContent = ({
|
|
|
29973
30113
|
player.pause();
|
|
29974
30114
|
}
|
|
29975
30115
|
};
|
|
29976
|
-
const
|
|
30116
|
+
const handlePlaybackSpeedChange = React23.useCallback((speed) => {
|
|
30117
|
+
setPlaybackSpeed(speed);
|
|
30118
|
+
if (videoRef.current?.playbackRate) {
|
|
30119
|
+
videoRef.current.playbackRate(speed);
|
|
30120
|
+
}
|
|
30121
|
+
}, []);
|
|
30122
|
+
React23.useCallback((e) => {
|
|
29977
30123
|
e.stopPropagation();
|
|
29978
30124
|
setIsFullscreen((prev) => !prev);
|
|
29979
30125
|
}, []);
|
|
@@ -30147,12 +30293,7 @@ var BottlenecksContent = ({
|
|
|
30147
30293
|
onLoadedData: handleLoadedData,
|
|
30148
30294
|
onPlaying: handleVideoPlaying,
|
|
30149
30295
|
onLoadingChange: handleVideoLoadingChange,
|
|
30150
|
-
options:
|
|
30151
|
-
// Ensure full height is always visible - no cropping
|
|
30152
|
-
fluid: false,
|
|
30153
|
-
responsive: false,
|
|
30154
|
-
fill: false
|
|
30155
|
-
}
|
|
30296
|
+
options: videoPlayerOptions
|
|
30156
30297
|
}
|
|
30157
30298
|
)
|
|
30158
30299
|
}
|
|
@@ -30213,58 +30354,7 @@ var BottlenecksContent = ({
|
|
|
30213
30354
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium mr-2", children: getClipTypeLabel(currentVideo) }),
|
|
30214
30355
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
|
|
30215
30356
|
] }) })
|
|
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
|
-
] }) })
|
|
30357
|
+
)
|
|
30268
30358
|
] }) }) }) : (
|
|
30269
30359
|
/* Priority 5: Show "no clips found" only if we have counts and there are truly no clips for workspace */
|
|
30270
30360
|
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 +30572,7 @@ var BottlenecksContent = ({
|
|
|
30482
30572
|
onLoadedData: handleLoadedData,
|
|
30483
30573
|
onPlaying: handleVideoPlaying,
|
|
30484
30574
|
onLoadingChange: handleVideoLoadingChange,
|
|
30485
|
-
options:
|
|
30486
|
-
fluid: false,
|
|
30487
|
-
responsive: false,
|
|
30488
|
-
fill: false
|
|
30489
|
-
}
|
|
30575
|
+
options: videoPlayerOptions
|
|
30490
30576
|
}
|
|
30491
30577
|
)
|
|
30492
30578
|
}
|
|
@@ -30494,45 +30580,32 @@ var BottlenecksContent = ({
|
|
|
30494
30580
|
(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
30581
|
!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
30582
|
/* @__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
|
-
|
|
30583
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
30584
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Speed:" }),
|
|
30585
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
30586
|
+
"select",
|
|
30500
30587
|
{
|
|
30501
|
-
|
|
30502
|
-
|
|
30503
|
-
|
|
30504
|
-
|
|
30505
|
-
|
|
30506
|
-
|
|
30507
|
-
|
|
30588
|
+
value: playbackSpeed,
|
|
30589
|
+
onChange: (e) => handlePlaybackSpeedChange(Number(e.target.value)),
|
|
30590
|
+
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",
|
|
30591
|
+
"aria-label": "Playback speed",
|
|
30592
|
+
children: [
|
|
30593
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "0.25", children: "0.25x" }),
|
|
30594
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "0.5", children: "0.5x" }),
|
|
30595
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "0.75", children: "0.75x" }),
|
|
30596
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "1", children: "1x" }),
|
|
30597
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "1.25", children: "1.25x" }),
|
|
30598
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "1.5", children: "1.5x" }),
|
|
30599
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "1.75", children: "1.75x" }),
|
|
30600
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "2", children: "2x" }),
|
|
30601
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "3", children: "3x" }),
|
|
30602
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "4", children: "4x" }),
|
|
30603
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "5", children: "5x" }),
|
|
30604
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "10", children: "10x" })
|
|
30605
|
+
]
|
|
30508
30606
|
}
|
|
30509
|
-
)
|
|
30510
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-mono px-2", children: [
|
|
30511
|
-
formatTime2(currentTime),
|
|
30512
|
-
" / ",
|
|
30513
|
-
formatTime2(duration)
|
|
30514
|
-
] })
|
|
30607
|
+
)
|
|
30515
30608
|
] }),
|
|
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
30609
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
30537
30610
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30538
30611
|
"button",
|
|
@@ -37633,26 +37706,26 @@ var SingleVideoStream = ({
|
|
|
37633
37706
|
const hlsStreamUrl = streamUrl || getCameraStreamUrl(workspaceName, baseUrl);
|
|
37634
37707
|
console.log(`Using camera URL for ${workspaceName}: ${hlsStreamUrl}`);
|
|
37635
37708
|
const mergedHlsConfig = { ...DEFAULT_HLS_CONFIG, ...hlsConfig };
|
|
37636
|
-
if (
|
|
37637
|
-
const hls = new
|
|
37709
|
+
if (Hls3__default.default.isSupported()) {
|
|
37710
|
+
const hls = new Hls3__default.default(mergedHlsConfig);
|
|
37638
37711
|
hlsRef.current = hls;
|
|
37639
|
-
hls.on(
|
|
37712
|
+
hls.on(Hls3__default.default.Events.MEDIA_ATTACHED, () => {
|
|
37640
37713
|
console.log("HLS media attached");
|
|
37641
37714
|
hls.loadSource(hlsStreamUrl);
|
|
37642
37715
|
});
|
|
37643
|
-
hls.on(
|
|
37716
|
+
hls.on(Hls3__default.default.Events.MANIFEST_PARSED, () => {
|
|
37644
37717
|
console.log("HLS manifest parsed");
|
|
37645
37718
|
attemptPlay(video);
|
|
37646
37719
|
});
|
|
37647
|
-
hls.on(
|
|
37720
|
+
hls.on(Hls3__default.default.Events.ERROR, (_, data) => {
|
|
37648
37721
|
if (data.fatal) {
|
|
37649
37722
|
console.error("Fatal HLS error:", data.type, data.details);
|
|
37650
37723
|
switch (data.type) {
|
|
37651
|
-
case
|
|
37724
|
+
case Hls3__default.default.ErrorTypes.NETWORK_ERROR:
|
|
37652
37725
|
console.error("Fatal network error encountered");
|
|
37653
37726
|
setError("Network error: Please check your connection");
|
|
37654
37727
|
break;
|
|
37655
|
-
case
|
|
37728
|
+
case Hls3__default.default.ErrorTypes.MEDIA_ERROR:
|
|
37656
37729
|
console.error("Fatal media error encountered, trying to recover");
|
|
37657
37730
|
hls.recoverMediaError();
|
|
37658
37731
|
break;
|
|
@@ -42413,9 +42486,9 @@ var MetricCards = React23.memo(({ lineInfo }) => {
|
|
|
42413
42486
|
animate: "animate",
|
|
42414
42487
|
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
42488
|
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%-
|
|
42489
|
+
/* @__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: [
|
|
42490
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm sm:text-base font-semibold text-gray-700 mb-2 text-center", children: "Line Output" }),
|
|
42491
|
+
/* @__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
42492
|
OutputProgressChart,
|
|
42420
42493
|
{
|
|
42421
42494
|
currentOutput: lineInfo?.metrics.current_output || 0,
|
|
@@ -42423,9 +42496,9 @@ var MetricCards = React23.memo(({ lineInfo }) => {
|
|
|
42423
42496
|
}
|
|
42424
42497
|
) }) })
|
|
42425
42498
|
] }),
|
|
42426
|
-
/* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[
|
|
42499
|
+
/* @__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
42500
|
/* @__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%-
|
|
42501
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center h-[calc(100%-2.5rem)]", children: [
|
|
42429
42502
|
/* @__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
42503
|
/* @__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
42504
|
"/ ",
|
|
@@ -42433,9 +42506,9 @@ var MetricCards = React23.memo(({ lineInfo }) => {
|
|
|
42433
42506
|
] })
|
|
42434
42507
|
] })
|
|
42435
42508
|
] }),
|
|
42436
|
-
/* @__PURE__ */ jsxRuntime.jsxs(motion.div, { variants: itemVariants, className: "bg-white rounded-xl shadow-sm p-3 sm:p-4 overflow-hidden h-[
|
|
42509
|
+
/* @__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
42510
|
/* @__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%-
|
|
42511
|
+
/* @__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
42512
|
lineInfo?.metrics.avg_efficiency.toFixed(1),
|
|
42440
42513
|
"%"
|
|
42441
42514
|
] }) })
|
|
@@ -51992,6 +52065,7 @@ exports.CardTitle = CardTitle2;
|
|
|
51992
52065
|
exports.ClipFilterProvider = ClipFilterProvider;
|
|
51993
52066
|
exports.CompactWorkspaceHealthCard = CompactWorkspaceHealthCard;
|
|
51994
52067
|
exports.CongratulationsOverlay = CongratulationsOverlay;
|
|
52068
|
+
exports.CroppedHlsVideoPlayer = CroppedHlsVideoPlayer;
|
|
51995
52069
|
exports.CroppedVideoPlayer = CroppedVideoPlayer;
|
|
51996
52070
|
exports.CycleTimeChart = CycleTimeChart;
|
|
51997
52071
|
exports.CycleTimeOverTimeChart = CycleTimeOverTimeChart;
|
|
@@ -52032,6 +52106,7 @@ exports.Header = Header;
|
|
|
52032
52106
|
exports.HealthStatusGrid = HealthStatusGrid;
|
|
52033
52107
|
exports.HealthStatusIndicator = HealthStatusIndicator;
|
|
52034
52108
|
exports.HelpView = HelpView_default;
|
|
52109
|
+
exports.HlsVideoPlayer = HlsVideoPlayer;
|
|
52035
52110
|
exports.HomeView = HomeView_default;
|
|
52036
52111
|
exports.HourlyOutputChart = HourlyOutputChart2;
|
|
52037
52112
|
exports.ISTTimer = ISTTimer_default;
|