@optifye/dashboard-core 6.6.9 → 6.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +3 -0
- package/dist/index.d.mts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +222 -77
- package/dist/index.mjs +222 -77
- package/package.json +1 -1
package/dist/index.css
CHANGED
|
@@ -2148,6 +2148,9 @@ body {
|
|
|
2148
2148
|
.bg-black\/80 {
|
|
2149
2149
|
background-color: rgb(0 0 0 / 0.8);
|
|
2150
2150
|
}
|
|
2151
|
+
.bg-black\/90 {
|
|
2152
|
+
background-color: rgb(0 0 0 / 0.9);
|
|
2153
|
+
}
|
|
2151
2154
|
.bg-blue-100 {
|
|
2152
2155
|
--tw-bg-opacity: 1;
|
|
2153
2156
|
background-color: rgb(219 234 254 / var(--tw-bg-opacity, 1));
|
package/dist/index.d.mts
CHANGED
|
@@ -1299,11 +1299,11 @@ declare class S3ClipsSupabaseService {
|
|
|
1299
1299
|
/**
|
|
1300
1300
|
* Get clip counts with optional video index
|
|
1301
1301
|
*/
|
|
1302
|
-
getClipCountsCacheFirst(workspaceId: string, date: string, shiftId: string | number, buildIndex?: boolean): Promise<ClipCountsWithIndex | Record<string, number>>;
|
|
1302
|
+
getClipCountsCacheFirst(workspaceId: string, date: string, shiftId: string | number, buildIndex?: boolean, totalOutput?: number): Promise<ClipCountsWithIndex | Record<string, number>>;
|
|
1303
1303
|
/**
|
|
1304
1304
|
* Get clip counts (simplified version)
|
|
1305
1305
|
*/
|
|
1306
|
-
getClipCounts(workspaceId: string, date: string, shiftId: string | number): Promise<Record<string, number>>;
|
|
1306
|
+
getClipCounts(workspaceId: string, date: string, shiftId: string | number, totalOutput?: number): Promise<Record<string, number>>;
|
|
1307
1307
|
/**
|
|
1308
1308
|
* Get clip by ID - stable navigation method
|
|
1309
1309
|
* This ensures navigation works even when new clips are added
|
|
@@ -3257,7 +3257,7 @@ declare function useClipTypes(): UseClipTypesResult;
|
|
|
3257
3257
|
/**
|
|
3258
3258
|
* Hook to get clip types with counts for a specific workspace/date/shift
|
|
3259
3259
|
*/
|
|
3260
|
-
declare function useClipTypesWithCounts(workspaceId: string, date: string, shiftId: string | number): UseClipTypesResult & {
|
|
3260
|
+
declare function useClipTypesWithCounts(workspaceId: string, date: string, shiftId: string | number, totalOutput?: number): UseClipTypesResult & {
|
|
3261
3261
|
counts: Record<string, number>;
|
|
3262
3262
|
};
|
|
3263
3263
|
|
|
@@ -5334,6 +5334,10 @@ interface BottlenecksContentProps {
|
|
|
5334
5334
|
* Optional className for styling
|
|
5335
5335
|
*/
|
|
5336
5336
|
className?: string;
|
|
5337
|
+
/**
|
|
5338
|
+
* Total output from workspace metrics for cycle completion adjustment
|
|
5339
|
+
*/
|
|
5340
|
+
totalOutput?: number;
|
|
5337
5341
|
}
|
|
5338
5342
|
/**
|
|
5339
5343
|
* Filter type for bottleneck clips - expanded for new video types
|
package/dist/index.d.ts
CHANGED
|
@@ -1299,11 +1299,11 @@ declare class S3ClipsSupabaseService {
|
|
|
1299
1299
|
/**
|
|
1300
1300
|
* Get clip counts with optional video index
|
|
1301
1301
|
*/
|
|
1302
|
-
getClipCountsCacheFirst(workspaceId: string, date: string, shiftId: string | number, buildIndex?: boolean): Promise<ClipCountsWithIndex | Record<string, number>>;
|
|
1302
|
+
getClipCountsCacheFirst(workspaceId: string, date: string, shiftId: string | number, buildIndex?: boolean, totalOutput?: number): Promise<ClipCountsWithIndex | Record<string, number>>;
|
|
1303
1303
|
/**
|
|
1304
1304
|
* Get clip counts (simplified version)
|
|
1305
1305
|
*/
|
|
1306
|
-
getClipCounts(workspaceId: string, date: string, shiftId: string | number): Promise<Record<string, number>>;
|
|
1306
|
+
getClipCounts(workspaceId: string, date: string, shiftId: string | number, totalOutput?: number): Promise<Record<string, number>>;
|
|
1307
1307
|
/**
|
|
1308
1308
|
* Get clip by ID - stable navigation method
|
|
1309
1309
|
* This ensures navigation works even when new clips are added
|
|
@@ -3257,7 +3257,7 @@ declare function useClipTypes(): UseClipTypesResult;
|
|
|
3257
3257
|
/**
|
|
3258
3258
|
* Hook to get clip types with counts for a specific workspace/date/shift
|
|
3259
3259
|
*/
|
|
3260
|
-
declare function useClipTypesWithCounts(workspaceId: string, date: string, shiftId: string | number): UseClipTypesResult & {
|
|
3260
|
+
declare function useClipTypesWithCounts(workspaceId: string, date: string, shiftId: string | number, totalOutput?: number): UseClipTypesResult & {
|
|
3261
3261
|
counts: Record<string, number>;
|
|
3262
3262
|
};
|
|
3263
3263
|
|
|
@@ -5334,6 +5334,10 @@ interface BottlenecksContentProps {
|
|
|
5334
5334
|
* Optional className for styling
|
|
5335
5335
|
*/
|
|
5336
5336
|
className?: string;
|
|
5337
|
+
/**
|
|
5338
|
+
* Total output from workspace metrics for cycle completion adjustment
|
|
5339
|
+
*/
|
|
5340
|
+
totalOutput?: number;
|
|
5337
5341
|
}
|
|
5338
5342
|
/**
|
|
5339
5343
|
* Filter type for bottleneck clips - expanded for new video types
|
package/dist/index.js
CHANGED
|
@@ -3765,7 +3765,7 @@ var S3ClipsSupabaseService = class {
|
|
|
3765
3765
|
/**
|
|
3766
3766
|
* Get clip counts with optional video index
|
|
3767
3767
|
*/
|
|
3768
|
-
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex = false) {
|
|
3768
|
+
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex = false, totalOutput) {
|
|
3769
3769
|
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
3770
3770
|
return this.deduplicate(cacheKey, async () => {
|
|
3771
3771
|
console.log(`[S3ClipsSupabase] Fetching clip counts from Supabase for:`, {
|
|
@@ -3776,7 +3776,8 @@ var S3ClipsSupabaseService = class {
|
|
|
3776
3776
|
const response = await this.fetchWithAuth("count", {
|
|
3777
3777
|
workspaceId,
|
|
3778
3778
|
date,
|
|
3779
|
-
shift: shiftId.toString()
|
|
3779
|
+
shift: shiftId.toString(),
|
|
3780
|
+
totalOutput
|
|
3780
3781
|
});
|
|
3781
3782
|
console.log(`[S3ClipsSupabase] Count API response:`, response);
|
|
3782
3783
|
const counts = response.counts || {};
|
|
@@ -3828,8 +3829,8 @@ var S3ClipsSupabaseService = class {
|
|
|
3828
3829
|
/**
|
|
3829
3830
|
* Get clip counts (simplified version)
|
|
3830
3831
|
*/
|
|
3831
|
-
async getClipCounts(workspaceId, date, shiftId) {
|
|
3832
|
-
const result = await this.getClipCountsCacheFirst(workspaceId, date, shiftId, false);
|
|
3832
|
+
async getClipCounts(workspaceId, date, shiftId, totalOutput) {
|
|
3833
|
+
const result = await this.getClipCountsCacheFirst(workspaceId, date, shiftId, false, totalOutput);
|
|
3833
3834
|
if (typeof result === "object" && "counts" in result) {
|
|
3834
3835
|
return result.counts;
|
|
3835
3836
|
}
|
|
@@ -9020,7 +9021,7 @@ function useClipTypes() {
|
|
|
9020
9021
|
refresh: fetchClipTypes
|
|
9021
9022
|
};
|
|
9022
9023
|
}
|
|
9023
|
-
function useClipTypesWithCounts(workspaceId, date, shiftId) {
|
|
9024
|
+
function useClipTypesWithCounts(workspaceId, date, shiftId, totalOutput) {
|
|
9024
9025
|
const { clipTypes, isLoading: typesLoading, error: typesError, refresh } = useClipTypes();
|
|
9025
9026
|
const [counts, setCounts] = React21.useState({});
|
|
9026
9027
|
const [countsLoading, setCountsLoading] = React21.useState(false);
|
|
@@ -9045,7 +9046,8 @@ function useClipTypesWithCounts(workspaceId, date, shiftId) {
|
|
|
9045
9046
|
const clipCounts = await s3Service.getClipCounts(
|
|
9046
9047
|
workspaceId,
|
|
9047
9048
|
date,
|
|
9048
|
-
shiftId.toString()
|
|
9049
|
+
shiftId.toString(),
|
|
9050
|
+
totalOutput
|
|
9049
9051
|
);
|
|
9050
9052
|
console.log("[useClipTypesWithCounts] Received counts:", clipCounts);
|
|
9051
9053
|
setCounts(clipCounts);
|
|
@@ -9056,7 +9058,7 @@ function useClipTypesWithCounts(workspaceId, date, shiftId) {
|
|
|
9056
9058
|
}
|
|
9057
9059
|
};
|
|
9058
9060
|
fetchCounts();
|
|
9059
|
-
}, [s3Service, workspaceId, date, shiftId]);
|
|
9061
|
+
}, [s3Service, workspaceId, date, shiftId, totalOutput]);
|
|
9060
9062
|
return {
|
|
9061
9063
|
clipTypes: clipTypes.map((type) => ({
|
|
9062
9064
|
...type,
|
|
@@ -22277,7 +22279,7 @@ var WorkspaceMetricCardsImpl = ({
|
|
|
22277
22279
|
/* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm h-full min-h-[150px] sm:min-h-0", children: [
|
|
22278
22280
|
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
22279
22281
|
/* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6 sm:py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
22280
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
22282
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
22281
22283
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
22282
22284
|
"Standard: ",
|
|
22283
22285
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -25957,8 +25959,19 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25957
25959
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
25958
25960
|
const dailyData = [];
|
|
25959
25961
|
let maxOutput = 0;
|
|
25960
|
-
let
|
|
25961
|
-
let
|
|
25962
|
+
let lastSetTarget = 0;
|
|
25963
|
+
for (let day = daysInMonth; day >= 1; day--) {
|
|
25964
|
+
const dayData = data.find((d) => {
|
|
25965
|
+
const date = new Date(d.date);
|
|
25966
|
+
return date.getDate() === day;
|
|
25967
|
+
});
|
|
25968
|
+
const shiftData = dayData ? selectedShift === "day" ? dayData.dayShift : dayData.nightShift : null;
|
|
25969
|
+
const idealOutput = shiftData ? shiftData.idealOutput : 0;
|
|
25970
|
+
if (idealOutput > 0) {
|
|
25971
|
+
lastSetTarget = idealOutput;
|
|
25972
|
+
break;
|
|
25973
|
+
}
|
|
25974
|
+
}
|
|
25962
25975
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
25963
25976
|
const dayData = data.find((d) => {
|
|
25964
25977
|
const date = new Date(d.date);
|
|
@@ -25968,11 +25981,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25968
25981
|
const output = shiftData && hasRealData(shiftData) ? shiftData.output : 0;
|
|
25969
25982
|
const idealOutput = shiftData ? shiftData.idealOutput : 0;
|
|
25970
25983
|
if (output > maxOutput) maxOutput = output;
|
|
25971
|
-
|
|
25972
|
-
totalIdealOutput += idealOutput;
|
|
25973
|
-
validDaysCount++;
|
|
25974
|
-
}
|
|
25975
|
-
const color2 = output >= idealOutput ? "#00AB45" : "#E34329";
|
|
25984
|
+
const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
|
|
25976
25985
|
dailyData.push({
|
|
25977
25986
|
hour: getOrdinal(day),
|
|
25978
25987
|
// Using ordinal format (1st, 2nd, 3rd, etc.)
|
|
@@ -25987,14 +25996,13 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25987
25996
|
// Not used but keeps structure consistent
|
|
25988
25997
|
});
|
|
25989
25998
|
}
|
|
25990
|
-
const
|
|
25991
|
-
const calculatedMax = Math.max(maxOutput, avgIdealOutput);
|
|
25999
|
+
const calculatedMax = Math.max(maxOutput, lastSetTarget);
|
|
25992
26000
|
const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
|
|
25993
|
-
return { data: dailyData, maxOutput,
|
|
26001
|
+
return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
|
|
25994
26002
|
}, [data, month, year, selectedShift]);
|
|
25995
26003
|
const yAxisTicks = React21.useMemo(() => {
|
|
25996
26004
|
const max = chartData.yAxisMax;
|
|
25997
|
-
const target = chartData.
|
|
26005
|
+
const target = chartData.lastSetTarget;
|
|
25998
26006
|
if (!max || max <= 0) return void 0;
|
|
25999
26007
|
const desiredIntervals = 4;
|
|
26000
26008
|
const roughStep = max / desiredIntervals;
|
|
@@ -26012,7 +26020,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26012
26020
|
ticks.push(Math.round(target));
|
|
26013
26021
|
}
|
|
26014
26022
|
return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
|
|
26015
|
-
}, [chartData.yAxisMax, chartData.
|
|
26023
|
+
}, [chartData.yAxisMax, chartData.lastSetTarget]);
|
|
26016
26024
|
const pieChartData = React21.useMemo(() => {
|
|
26017
26025
|
const validShifts = data.map((d) => selectedShift === "day" ? d.dayShift : d.nightShift).filter(hasRealData);
|
|
26018
26026
|
if (validShifts.length === 0) return [];
|
|
@@ -26324,7 +26332,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26324
26332
|
tick: (props) => {
|
|
26325
26333
|
const { x, y, payload } = props;
|
|
26326
26334
|
const value = Math.round(payload.value);
|
|
26327
|
-
const targetValue = Math.round(chartData.
|
|
26335
|
+
const targetValue = Math.round(chartData.lastSetTarget);
|
|
26328
26336
|
const isTarget = Math.abs(value - targetValue) < 1 && targetValue > 0;
|
|
26329
26337
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
26330
26338
|
"text",
|
|
@@ -26348,10 +26356,10 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26348
26356
|
content: CustomTooltip
|
|
26349
26357
|
}
|
|
26350
26358
|
),
|
|
26351
|
-
chartData.
|
|
26359
|
+
chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26352
26360
|
recharts.ReferenceLine,
|
|
26353
26361
|
{
|
|
26354
|
-
y: chartData.
|
|
26362
|
+
y: chartData.lastSetTarget,
|
|
26355
26363
|
stroke: "#E34329",
|
|
26356
26364
|
strokeDasharray: "5 5",
|
|
26357
26365
|
strokeWidth: 2
|
|
@@ -26409,11 +26417,11 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26409
26417
|
]
|
|
26410
26418
|
}
|
|
26411
26419
|
) }) }),
|
|
26412
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center gap-6 mt-3", children: chartData.
|
|
26420
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center gap-6 mt-3", children: chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
26413
26421
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-0.5 border-t-2 border-dashed", style: { borderColor: "#E34329" } }),
|
|
26414
26422
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-600", children: [
|
|
26415
26423
|
"Target: ",
|
|
26416
|
-
Math.round(chartData.
|
|
26424
|
+
Math.round(chartData.lastSetTarget),
|
|
26417
26425
|
" units/day"
|
|
26418
26426
|
] })
|
|
26419
26427
|
] }) })
|
|
@@ -27259,6 +27267,36 @@ var TimePickerDropdown = ({
|
|
|
27259
27267
|
] })
|
|
27260
27268
|
] });
|
|
27261
27269
|
};
|
|
27270
|
+
var ERROR_MAPPING = {
|
|
27271
|
+
1: {
|
|
27272
|
+
// MEDIA_ERR_ABORTED
|
|
27273
|
+
code: 1,
|
|
27274
|
+
type: "recoverable" /* RECOVERABLE */,
|
|
27275
|
+
message: "Video loading was interrupted",
|
|
27276
|
+
canRetry: true
|
|
27277
|
+
},
|
|
27278
|
+
2: {
|
|
27279
|
+
// MEDIA_ERR_NETWORK
|
|
27280
|
+
code: 2,
|
|
27281
|
+
type: "recoverable" /* RECOVERABLE */,
|
|
27282
|
+
message: "Network error - please check your internet connection",
|
|
27283
|
+
canRetry: true
|
|
27284
|
+
},
|
|
27285
|
+
3: {
|
|
27286
|
+
// MEDIA_ERR_DECODE
|
|
27287
|
+
code: 3,
|
|
27288
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27289
|
+
message: "Stream corrupted due to internet connection",
|
|
27290
|
+
canRetry: false
|
|
27291
|
+
},
|
|
27292
|
+
4: {
|
|
27293
|
+
// MEDIA_ERR_SRC_NOT_SUPPORTED
|
|
27294
|
+
code: 4,
|
|
27295
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27296
|
+
message: "Video format not supported by your browser. Please use Google Chrome.",
|
|
27297
|
+
canRetry: false
|
|
27298
|
+
}
|
|
27299
|
+
};
|
|
27262
27300
|
var videoPlayerStyles = `
|
|
27263
27301
|
.video-player-container {
|
|
27264
27302
|
width: 100%;
|
|
@@ -27489,8 +27527,21 @@ var VideoPlayer = React21__namespace.default.forwardRef(({
|
|
|
27489
27527
|
player.on("seeked", () => onSeeked?.(player));
|
|
27490
27528
|
player.on("error", () => {
|
|
27491
27529
|
const error = player.error();
|
|
27492
|
-
|
|
27493
|
-
|
|
27530
|
+
const errorCode = error?.code ?? 0;
|
|
27531
|
+
const errorInfo = ERROR_MAPPING[errorCode] || {
|
|
27532
|
+
code: errorCode || 0,
|
|
27533
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27534
|
+
message: "Unknown playback error occurred",
|
|
27535
|
+
canRetry: false
|
|
27536
|
+
};
|
|
27537
|
+
console.error("[VideoPlayer] Video.js error:", {
|
|
27538
|
+
code: errorCode,
|
|
27539
|
+
type: errorInfo.type,
|
|
27540
|
+
message: errorInfo.message,
|
|
27541
|
+
canRetry: errorInfo.canRetry,
|
|
27542
|
+
originalError: error
|
|
27543
|
+
});
|
|
27544
|
+
onError?.(player, errorInfo);
|
|
27494
27545
|
});
|
|
27495
27546
|
if (src) {
|
|
27496
27547
|
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
@@ -28668,7 +28719,7 @@ var FileManagerFilters = ({
|
|
|
28668
28719
|
];
|
|
28669
28720
|
percentileCategories.forEach((category) => {
|
|
28670
28721
|
if (category.count > 0 && shouldShowCategory(category.id)) {
|
|
28671
|
-
tree.
|
|
28722
|
+
tree.push(category);
|
|
28672
28723
|
}
|
|
28673
28724
|
});
|
|
28674
28725
|
return tree;
|
|
@@ -29223,7 +29274,8 @@ var BottlenecksContent = ({
|
|
|
29223
29274
|
workspaceName,
|
|
29224
29275
|
date,
|
|
29225
29276
|
shift,
|
|
29226
|
-
className
|
|
29277
|
+
className,
|
|
29278
|
+
totalOutput
|
|
29227
29279
|
}) => {
|
|
29228
29280
|
const dashboardConfig = useDashboardConfig();
|
|
29229
29281
|
const timezone = useAppTimezone();
|
|
@@ -29322,8 +29374,9 @@ var BottlenecksContent = ({
|
|
|
29322
29374
|
} = useClipTypesWithCounts(
|
|
29323
29375
|
workspaceId,
|
|
29324
29376
|
date || getOperationalDate(timezone),
|
|
29325
|
-
effectiveShift
|
|
29377
|
+
effectiveShift,
|
|
29326
29378
|
// Use same shift as video loading for consistency
|
|
29379
|
+
totalOutput
|
|
29327
29380
|
);
|
|
29328
29381
|
console.log("[BottlenecksContent] Clip types data:", {
|
|
29329
29382
|
clipTypes,
|
|
@@ -29368,8 +29421,9 @@ var BottlenecksContent = ({
|
|
|
29368
29421
|
const fullResult = await s3ClipsService.getClipCounts(
|
|
29369
29422
|
workspaceId,
|
|
29370
29423
|
operationalDate,
|
|
29371
|
-
shiftStr
|
|
29372
|
-
|
|
29424
|
+
shiftStr,
|
|
29425
|
+
totalOutput
|
|
29426
|
+
// Pass totalOutput for cycle completion adjustment
|
|
29373
29427
|
);
|
|
29374
29428
|
console.log(`[BottlenecksContent] Direct fetch result:`, fullResult);
|
|
29375
29429
|
if (fullResult) {
|
|
@@ -29382,7 +29436,12 @@ var BottlenecksContent = ({
|
|
|
29382
29436
|
} catch (err) {
|
|
29383
29437
|
console.error("[BottlenecksContent] Error fetching clip counts:", err);
|
|
29384
29438
|
if (isMountedRef.current) {
|
|
29385
|
-
setError(
|
|
29439
|
+
setError({
|
|
29440
|
+
type: "fatal",
|
|
29441
|
+
message: "Failed to load clip counts. Please try again.",
|
|
29442
|
+
canSkip: false,
|
|
29443
|
+
canRetry: true
|
|
29444
|
+
});
|
|
29386
29445
|
setIsLoading(false);
|
|
29387
29446
|
}
|
|
29388
29447
|
} finally {
|
|
@@ -29466,7 +29525,12 @@ var BottlenecksContent = ({
|
|
|
29466
29525
|
} catch (err) {
|
|
29467
29526
|
console.error("Error loading first video for category:", err);
|
|
29468
29527
|
if (isMountedRef.current) {
|
|
29469
|
-
setError(
|
|
29528
|
+
setError({
|
|
29529
|
+
type: "fatal",
|
|
29530
|
+
message: "Failed to load clips. Please try again.",
|
|
29531
|
+
canSkip: false,
|
|
29532
|
+
canRetry: true
|
|
29533
|
+
});
|
|
29470
29534
|
setIsCategoryLoading(false);
|
|
29471
29535
|
}
|
|
29472
29536
|
} finally {
|
|
@@ -29748,7 +29812,12 @@ var BottlenecksContent = ({
|
|
|
29748
29812
|
} catch (error2) {
|
|
29749
29813
|
console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
|
|
29750
29814
|
if (isMountedRef.current) {
|
|
29751
|
-
setError(
|
|
29815
|
+
setError({
|
|
29816
|
+
type: "fatal",
|
|
29817
|
+
message: "Failed to load selected clip. Please try again.",
|
|
29818
|
+
canSkip: true,
|
|
29819
|
+
canRetry: true
|
|
29820
|
+
});
|
|
29752
29821
|
clearLoadingState();
|
|
29753
29822
|
}
|
|
29754
29823
|
}
|
|
@@ -29771,7 +29840,12 @@ var BottlenecksContent = ({
|
|
|
29771
29840
|
}
|
|
29772
29841
|
} catch (error2) {
|
|
29773
29842
|
console.error(`[BottlenecksContent] Error in legacy loadAndPlayClip:`, error2);
|
|
29774
|
-
setError(
|
|
29843
|
+
setError({
|
|
29844
|
+
type: "fatal",
|
|
29845
|
+
message: "Failed to load selected clip. Please try again.",
|
|
29846
|
+
canSkip: true,
|
|
29847
|
+
canRetry: true
|
|
29848
|
+
});
|
|
29775
29849
|
setIsNavigating(false);
|
|
29776
29850
|
}
|
|
29777
29851
|
}, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById]);
|
|
@@ -29811,7 +29885,12 @@ var BottlenecksContent = ({
|
|
|
29811
29885
|
}
|
|
29812
29886
|
} catch (error2) {
|
|
29813
29887
|
console.error(`[handleNext] Error navigating:`, error2);
|
|
29814
|
-
setError(
|
|
29888
|
+
setError({
|
|
29889
|
+
type: "fatal",
|
|
29890
|
+
message: "Failed to navigate to next clip",
|
|
29891
|
+
canSkip: true,
|
|
29892
|
+
canRetry: true
|
|
29893
|
+
});
|
|
29815
29894
|
clearLoadingState();
|
|
29816
29895
|
}
|
|
29817
29896
|
}, [clearLoadingState, s3ClipsService]);
|
|
@@ -29847,7 +29926,12 @@ var BottlenecksContent = ({
|
|
|
29847
29926
|
}
|
|
29848
29927
|
} catch (error2) {
|
|
29849
29928
|
console.error(`[handlePrevious] Error navigating:`, error2);
|
|
29850
|
-
setError(
|
|
29929
|
+
setError({
|
|
29930
|
+
type: "fatal",
|
|
29931
|
+
message: "Failed to navigate to previous clip",
|
|
29932
|
+
canSkip: true,
|
|
29933
|
+
canRetry: true
|
|
29934
|
+
});
|
|
29851
29935
|
clearLoadingState();
|
|
29852
29936
|
}
|
|
29853
29937
|
}, [clearLoadingState, s3ClipsService]);
|
|
@@ -29860,7 +29944,7 @@ var BottlenecksContent = ({
|
|
|
29860
29944
|
const handleVideoReady = React21.useCallback((player) => {
|
|
29861
29945
|
console.log("Video.js player ready - NOT clearing loading (wait for playing event)");
|
|
29862
29946
|
videoRetryCountRef.current = 0;
|
|
29863
|
-
if (error?.
|
|
29947
|
+
if (error?.isRetrying) {
|
|
29864
29948
|
setError(null);
|
|
29865
29949
|
}
|
|
29866
29950
|
}, [error]);
|
|
@@ -29905,20 +29989,53 @@ var BottlenecksContent = ({
|
|
|
29905
29989
|
}, [clearLoadingState]);
|
|
29906
29990
|
const handleVideoLoadingChange = React21.useCallback((isLoading2) => {
|
|
29907
29991
|
console.log(`[BottlenecksContent] Video loading state changed: ${isLoading2}`);
|
|
29992
|
+
if (error && error.type === "fatal") {
|
|
29993
|
+
console.log(`[BottlenecksContent] Ignoring loading state change - fatal error is showing`);
|
|
29994
|
+
return;
|
|
29995
|
+
}
|
|
29908
29996
|
setIsVideoBuffering(isLoading2);
|
|
29909
|
-
}, []);
|
|
29997
|
+
}, [error]);
|
|
29910
29998
|
const handleVideoEnded = React21.useCallback((player) => {
|
|
29911
29999
|
handleNext();
|
|
29912
30000
|
}, [handleNext]);
|
|
29913
30001
|
const videoRetryCountRef = React21.useRef(0);
|
|
29914
|
-
const handleVideoError = React21.useCallback((player,
|
|
29915
|
-
console.error("Video.js error:",
|
|
30002
|
+
const handleVideoError = React21.useCallback((player, errorInfo) => {
|
|
30003
|
+
console.error("[BottlenecksContent] Video.js error:", errorInfo);
|
|
29916
30004
|
setIsPlaying(false);
|
|
30005
|
+
setIsVideoBuffering(false);
|
|
30006
|
+
const errorCode = errorInfo?.code || 0;
|
|
30007
|
+
const canRetry = errorInfo?.canRetry ?? false;
|
|
30008
|
+
const errorMessage = errorInfo?.message || "Unknown error";
|
|
30009
|
+
console.log(`[Video Error] Code: ${errorCode}, Can Retry: ${canRetry}, Message: ${errorMessage}`);
|
|
30010
|
+
if (!canRetry) {
|
|
30011
|
+
console.log("[Video Error] Non-recoverable error - showing error overlay immediately");
|
|
30012
|
+
setError({
|
|
30013
|
+
type: "fatal",
|
|
30014
|
+
code: errorCode,
|
|
30015
|
+
message: errorMessage,
|
|
30016
|
+
canSkip: true,
|
|
30017
|
+
canRetry: false
|
|
30018
|
+
});
|
|
30019
|
+
clearLoadingState();
|
|
30020
|
+
videoRetryCountRef.current = 0;
|
|
30021
|
+
trackCoreEvent("clips_video_error_non_recoverable", {
|
|
30022
|
+
workspaceId,
|
|
30023
|
+
category: activeFilterRef.current,
|
|
30024
|
+
videoId: currentVideo?.id,
|
|
30025
|
+
errorCode,
|
|
30026
|
+
errorMessage
|
|
30027
|
+
});
|
|
30028
|
+
return;
|
|
30029
|
+
}
|
|
29917
30030
|
if (videoRetryCountRef.current < 3 && currentVideo) {
|
|
29918
30031
|
videoRetryCountRef.current++;
|
|
29919
30032
|
const retryDelay = 1e3 * videoRetryCountRef.current;
|
|
29920
|
-
console.log(`[Video Error] Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
|
|
29921
|
-
setError(
|
|
30033
|
+
console.log(`[Video Error] Recoverable error - Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
|
|
30034
|
+
setError({
|
|
30035
|
+
type: "retrying",
|
|
30036
|
+
message: `Retrying... (${videoRetryCountRef.current}/3)`,
|
|
30037
|
+
isRetrying: true
|
|
30038
|
+
});
|
|
29922
30039
|
setTimeout(() => {
|
|
29923
30040
|
if (videoRef.current && currentVideo && isMountedRef.current) {
|
|
29924
30041
|
setError(null);
|
|
@@ -29926,16 +30043,26 @@ var BottlenecksContent = ({
|
|
|
29926
30043
|
}
|
|
29927
30044
|
}, retryDelay);
|
|
29928
30045
|
} else {
|
|
29929
|
-
|
|
30046
|
+
console.log("[Video Error] Retries exhausted - showing final error overlay");
|
|
30047
|
+
setError({
|
|
30048
|
+
type: "fatal",
|
|
30049
|
+
code: errorCode,
|
|
30050
|
+
message: errorMessage,
|
|
30051
|
+
canSkip: true,
|
|
30052
|
+
canRetry: true
|
|
30053
|
+
// Allow manual retry for network errors
|
|
30054
|
+
});
|
|
29930
30055
|
videoRetryCountRef.current = 0;
|
|
29931
30056
|
trackCoreEvent("clips_video_error_final", {
|
|
29932
30057
|
workspaceId,
|
|
29933
30058
|
category: activeFilterRef.current,
|
|
29934
30059
|
videoId: currentVideo?.id,
|
|
29935
|
-
|
|
30060
|
+
errorCode,
|
|
30061
|
+
errorMessage,
|
|
30062
|
+
attempts: 3
|
|
29936
30063
|
});
|
|
29937
30064
|
}
|
|
29938
|
-
}, [currentVideo, workspaceId]);
|
|
30065
|
+
}, [currentVideo, workspaceId, clearLoadingState]);
|
|
29939
30066
|
React21.useEffect(() => {
|
|
29940
30067
|
isMountedRef.current = true;
|
|
29941
30068
|
return () => {
|
|
@@ -29953,9 +30080,13 @@ var BottlenecksContent = ({
|
|
|
29953
30080
|
}, [s3ClipsService]);
|
|
29954
30081
|
React21.useEffect(() => {
|
|
29955
30082
|
if (filteredVideos.length > 0 && currentIndex < filteredVideos.length) {
|
|
30083
|
+
if (error && error.type === "fatal") {
|
|
30084
|
+
console.log("[BottlenecksContent] Not clearing fatal error on video change - let user handle it");
|
|
30085
|
+
return;
|
|
30086
|
+
}
|
|
29956
30087
|
setError(null);
|
|
29957
30088
|
}
|
|
29958
|
-
}, [currentIndex, filteredVideos]);
|
|
30089
|
+
}, [currentIndex, filteredVideos, error]);
|
|
29959
30090
|
React21.useEffect(() => {
|
|
29960
30091
|
if (!isTransitioning && pendingVideo) {
|
|
29961
30092
|
const timer = setTimeout(() => {
|
|
@@ -30030,11 +30161,11 @@ var BottlenecksContent = ({
|
|
|
30030
30161
|
if ((isLoading || clipTypesLoading) && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
|
|
30031
30162
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-grow p-4 flex items-center justify-center h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
|
|
30032
30163
|
}
|
|
30033
|
-
if (error || clipTypesError) {
|
|
30164
|
+
if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
|
|
30034
30165
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
|
|
30035
30166
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
|
|
30036
30167
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "Error Loading Clips" }),
|
|
30037
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error || clipTypesError })
|
|
30168
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
|
|
30038
30169
|
] });
|
|
30039
30170
|
}
|
|
30040
30171
|
const categoriesToShow = clipTypes.length > 0 ? clipTypes : [];
|
|
@@ -30081,7 +30212,7 @@ var BottlenecksContent = ({
|
|
|
30081
30212
|
),
|
|
30082
30213
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
30083
30214
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children: categoryMetadata.length > 0 ? `${currentMetadataIndex + 1} / ${categoryMetadata.length}` : "0 / 0" }),
|
|
30084
|
-
error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-
|
|
30215
|
+
error && error.type === "retrying" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-orange-600 font-medium", children: error.message })
|
|
30085
30216
|
] }),
|
|
30086
30217
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30087
30218
|
"button",
|
|
@@ -30095,7 +30226,6 @@ var BottlenecksContent = ({
|
|
|
30095
30226
|
)
|
|
30096
30227
|
] })
|
|
30097
30228
|
] }) }),
|
|
30098
|
-
/* Show video if we have filtered videos and current video, or show loading */
|
|
30099
30229
|
filteredVideos.length > 0 && currentVideo ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative h-full group", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
|
|
30100
30230
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30101
30231
|
CroppedVideoPlayer,
|
|
@@ -30127,30 +30257,44 @@ var BottlenecksContent = ({
|
|
|
30127
30257
|
}
|
|
30128
30258
|
}
|
|
30129
30259
|
),
|
|
30130
|
-
(isTransitioning || isVideoBuffering && isInitialLoading) && /* @__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..." }) }),
|
|
30131
|
-
!isTransitioning && isVideoBuffering && !isInitialLoading && /* @__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..." }) }),
|
|
30132
|
-
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/
|
|
30260
|
+
(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..." }) }),
|
|
30261
|
+
!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..." }) }),
|
|
30262
|
+
error && error.type === "retrying" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
30263
|
+
/* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md" }),
|
|
30264
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
|
|
30265
|
+
] }) }),
|
|
30266
|
+
error && error.type === "fatal" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/90 text-white p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center max-w-md", children: [
|
|
30133
30267
|
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-16 h-16 mx-auto mb-4 text-red-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 16.5c-.77.833.192 2.5 1.732 2.5z" }) }),
|
|
30134
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: "
|
|
30135
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-300 mb-
|
|
30136
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
30137
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30138
|
-
|
|
30139
|
-
|
|
30140
|
-
|
|
30141
|
-
|
|
30142
|
-
|
|
30143
|
-
|
|
30144
|
-
|
|
30145
|
-
|
|
30146
|
-
|
|
30147
|
-
|
|
30148
|
-
|
|
30149
|
-
|
|
30150
|
-
|
|
30151
|
-
|
|
30152
|
-
|
|
30153
|
-
|
|
30268
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: error.code === 3 ? "Stream Corrupted" : error.code === 4 ? "Format Not Supported" : error.code === 2 ? "Network Error" : error.code === 1 ? "Loading Interrupted" : "Playback Error" }),
|
|
30269
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
|
|
30270
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 justify-center", children: [
|
|
30271
|
+
error.canSkip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
30272
|
+
"button",
|
|
30273
|
+
{
|
|
30274
|
+
onClick: () => {
|
|
30275
|
+
setError(null);
|
|
30276
|
+
videoRetryCountRef.current = 0;
|
|
30277
|
+
handleNext();
|
|
30278
|
+
},
|
|
30279
|
+
className: "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 rounded-md text-sm font-medium transition-colors",
|
|
30280
|
+
children: "Skip to Next Clip"
|
|
30281
|
+
}
|
|
30282
|
+
),
|
|
30283
|
+
error.canRetry && /* @__PURE__ */ jsxRuntime.jsx(
|
|
30284
|
+
"button",
|
|
30285
|
+
{
|
|
30286
|
+
onClick: () => {
|
|
30287
|
+
setError(null);
|
|
30288
|
+
videoRetryCountRef.current = 0;
|
|
30289
|
+
if (videoRef.current) {
|
|
30290
|
+
videoRef.current.dispose();
|
|
30291
|
+
}
|
|
30292
|
+
},
|
|
30293
|
+
className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
|
|
30294
|
+
children: "Retry"
|
|
30295
|
+
}
|
|
30296
|
+
)
|
|
30297
|
+
] })
|
|
30154
30298
|
] }) }),
|
|
30155
30299
|
(currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || currentVideo.type === "idle_time" || currentVideo.type === "low_value" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-3 left-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
30156
30300
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" || currentVideo.type === "idle_time" ? "bg-purple-400" : isPercentileCategory(activeFilterRef.current) ? activeFilterRef.current === "fast-cycles" ? "bg-green-600" : activeFilterRef.current === "slow-cycles" ? "bg-red-700" : "bg-orange-500" : currentVideo.type === "cycle_completion" ? "bg-blue-600" : "bg-gray-500"} mr-2 animate-pulse` }),
|
|
@@ -42659,7 +42803,7 @@ var WorkspaceDetailView = ({
|
|
|
42659
42803
|
/* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
|
|
42660
42804
|
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
42661
42805
|
/* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
42662
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42806
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42663
42807
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
42664
42808
|
"Standard: ",
|
|
42665
42809
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -42779,7 +42923,7 @@ var WorkspaceDetailView = ({
|
|
|
42779
42923
|
/* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
|
|
42780
42924
|
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
42781
42925
|
/* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
42782
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42926
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42783
42927
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
42784
42928
|
"Standard: ",
|
|
42785
42929
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -42854,6 +42998,7 @@ var WorkspaceDetailView = ({
|
|
|
42854
42998
|
workspaceName: formattedWorkspaceName,
|
|
42855
42999
|
date,
|
|
42856
43000
|
shift,
|
|
43001
|
+
totalOutput: workspace?.total_actions,
|
|
42857
43002
|
className: "h-[calc(100vh-10rem)]"
|
|
42858
43003
|
}
|
|
42859
43004
|
) })
|
package/dist/index.mjs
CHANGED
|
@@ -3735,7 +3735,7 @@ var S3ClipsSupabaseService = class {
|
|
|
3735
3735
|
/**
|
|
3736
3736
|
* Get clip counts with optional video index
|
|
3737
3737
|
*/
|
|
3738
|
-
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex = false) {
|
|
3738
|
+
async getClipCountsCacheFirst(workspaceId, date, shiftId, buildIndex = false, totalOutput) {
|
|
3739
3739
|
const cacheKey = `clip-counts:${workspaceId}:${date}:${shiftId}`;
|
|
3740
3740
|
return this.deduplicate(cacheKey, async () => {
|
|
3741
3741
|
console.log(`[S3ClipsSupabase] Fetching clip counts from Supabase for:`, {
|
|
@@ -3746,7 +3746,8 @@ var S3ClipsSupabaseService = class {
|
|
|
3746
3746
|
const response = await this.fetchWithAuth("count", {
|
|
3747
3747
|
workspaceId,
|
|
3748
3748
|
date,
|
|
3749
|
-
shift: shiftId.toString()
|
|
3749
|
+
shift: shiftId.toString(),
|
|
3750
|
+
totalOutput
|
|
3750
3751
|
});
|
|
3751
3752
|
console.log(`[S3ClipsSupabase] Count API response:`, response);
|
|
3752
3753
|
const counts = response.counts || {};
|
|
@@ -3798,8 +3799,8 @@ var S3ClipsSupabaseService = class {
|
|
|
3798
3799
|
/**
|
|
3799
3800
|
* Get clip counts (simplified version)
|
|
3800
3801
|
*/
|
|
3801
|
-
async getClipCounts(workspaceId, date, shiftId) {
|
|
3802
|
-
const result = await this.getClipCountsCacheFirst(workspaceId, date, shiftId, false);
|
|
3802
|
+
async getClipCounts(workspaceId, date, shiftId, totalOutput) {
|
|
3803
|
+
const result = await this.getClipCountsCacheFirst(workspaceId, date, shiftId, false, totalOutput);
|
|
3803
3804
|
if (typeof result === "object" && "counts" in result) {
|
|
3804
3805
|
return result.counts;
|
|
3805
3806
|
}
|
|
@@ -8990,7 +8991,7 @@ function useClipTypes() {
|
|
|
8990
8991
|
refresh: fetchClipTypes
|
|
8991
8992
|
};
|
|
8992
8993
|
}
|
|
8993
|
-
function useClipTypesWithCounts(workspaceId, date, shiftId) {
|
|
8994
|
+
function useClipTypesWithCounts(workspaceId, date, shiftId, totalOutput) {
|
|
8994
8995
|
const { clipTypes, isLoading: typesLoading, error: typesError, refresh } = useClipTypes();
|
|
8995
8996
|
const [counts, setCounts] = useState({});
|
|
8996
8997
|
const [countsLoading, setCountsLoading] = useState(false);
|
|
@@ -9015,7 +9016,8 @@ function useClipTypesWithCounts(workspaceId, date, shiftId) {
|
|
|
9015
9016
|
const clipCounts = await s3Service.getClipCounts(
|
|
9016
9017
|
workspaceId,
|
|
9017
9018
|
date,
|
|
9018
|
-
shiftId.toString()
|
|
9019
|
+
shiftId.toString(),
|
|
9020
|
+
totalOutput
|
|
9019
9021
|
);
|
|
9020
9022
|
console.log("[useClipTypesWithCounts] Received counts:", clipCounts);
|
|
9021
9023
|
setCounts(clipCounts);
|
|
@@ -9026,7 +9028,7 @@ function useClipTypesWithCounts(workspaceId, date, shiftId) {
|
|
|
9026
9028
|
}
|
|
9027
9029
|
};
|
|
9028
9030
|
fetchCounts();
|
|
9029
|
-
}, [s3Service, workspaceId, date, shiftId]);
|
|
9031
|
+
}, [s3Service, workspaceId, date, shiftId, totalOutput]);
|
|
9030
9032
|
return {
|
|
9031
9033
|
clipTypes: clipTypes.map((type) => ({
|
|
9032
9034
|
...type,
|
|
@@ -22247,7 +22249,7 @@ var WorkspaceMetricCardsImpl = ({
|
|
|
22247
22249
|
/* @__PURE__ */ jsxs(Card2, { className: "flex flex-col bg-white shadow-sm h-full min-h-[150px] sm:min-h-0", children: [
|
|
22248
22250
|
/* @__PURE__ */ jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
22249
22251
|
/* @__PURE__ */ jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6 sm:py-3", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
22250
|
-
/* @__PURE__ */ jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
22252
|
+
/* @__PURE__ */ jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
22251
22253
|
/* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
22252
22254
|
"Standard: ",
|
|
22253
22255
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -25927,8 +25929,19 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25927
25929
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
25928
25930
|
const dailyData = [];
|
|
25929
25931
|
let maxOutput = 0;
|
|
25930
|
-
let
|
|
25931
|
-
let
|
|
25932
|
+
let lastSetTarget = 0;
|
|
25933
|
+
for (let day = daysInMonth; day >= 1; day--) {
|
|
25934
|
+
const dayData = data.find((d) => {
|
|
25935
|
+
const date = new Date(d.date);
|
|
25936
|
+
return date.getDate() === day;
|
|
25937
|
+
});
|
|
25938
|
+
const shiftData = dayData ? selectedShift === "day" ? dayData.dayShift : dayData.nightShift : null;
|
|
25939
|
+
const idealOutput = shiftData ? shiftData.idealOutput : 0;
|
|
25940
|
+
if (idealOutput > 0) {
|
|
25941
|
+
lastSetTarget = idealOutput;
|
|
25942
|
+
break;
|
|
25943
|
+
}
|
|
25944
|
+
}
|
|
25932
25945
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
25933
25946
|
const dayData = data.find((d) => {
|
|
25934
25947
|
const date = new Date(d.date);
|
|
@@ -25938,11 +25951,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25938
25951
|
const output = shiftData && hasRealData(shiftData) ? shiftData.output : 0;
|
|
25939
25952
|
const idealOutput = shiftData ? shiftData.idealOutput : 0;
|
|
25940
25953
|
if (output > maxOutput) maxOutput = output;
|
|
25941
|
-
|
|
25942
|
-
totalIdealOutput += idealOutput;
|
|
25943
|
-
validDaysCount++;
|
|
25944
|
-
}
|
|
25945
|
-
const color2 = output >= idealOutput ? "#00AB45" : "#E34329";
|
|
25954
|
+
const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
|
|
25946
25955
|
dailyData.push({
|
|
25947
25956
|
hour: getOrdinal(day),
|
|
25948
25957
|
// Using ordinal format (1st, 2nd, 3rd, etc.)
|
|
@@ -25957,14 +25966,13 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25957
25966
|
// Not used but keeps structure consistent
|
|
25958
25967
|
});
|
|
25959
25968
|
}
|
|
25960
|
-
const
|
|
25961
|
-
const calculatedMax = Math.max(maxOutput, avgIdealOutput);
|
|
25969
|
+
const calculatedMax = Math.max(maxOutput, lastSetTarget);
|
|
25962
25970
|
const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
|
|
25963
|
-
return { data: dailyData, maxOutput,
|
|
25971
|
+
return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
|
|
25964
25972
|
}, [data, month, year, selectedShift]);
|
|
25965
25973
|
const yAxisTicks = useMemo(() => {
|
|
25966
25974
|
const max = chartData.yAxisMax;
|
|
25967
|
-
const target = chartData.
|
|
25975
|
+
const target = chartData.lastSetTarget;
|
|
25968
25976
|
if (!max || max <= 0) return void 0;
|
|
25969
25977
|
const desiredIntervals = 4;
|
|
25970
25978
|
const roughStep = max / desiredIntervals;
|
|
@@ -25982,7 +25990,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25982
25990
|
ticks.push(Math.round(target));
|
|
25983
25991
|
}
|
|
25984
25992
|
return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
|
|
25985
|
-
}, [chartData.yAxisMax, chartData.
|
|
25993
|
+
}, [chartData.yAxisMax, chartData.lastSetTarget]);
|
|
25986
25994
|
const pieChartData = useMemo(() => {
|
|
25987
25995
|
const validShifts = data.map((d) => selectedShift === "day" ? d.dayShift : d.nightShift).filter(hasRealData);
|
|
25988
25996
|
if (validShifts.length === 0) return [];
|
|
@@ -26294,7 +26302,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26294
26302
|
tick: (props) => {
|
|
26295
26303
|
const { x, y, payload } = props;
|
|
26296
26304
|
const value = Math.round(payload.value);
|
|
26297
|
-
const targetValue = Math.round(chartData.
|
|
26305
|
+
const targetValue = Math.round(chartData.lastSetTarget);
|
|
26298
26306
|
const isTarget = Math.abs(value - targetValue) < 1 && targetValue > 0;
|
|
26299
26307
|
return /* @__PURE__ */ jsx(
|
|
26300
26308
|
"text",
|
|
@@ -26318,10 +26326,10 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26318
26326
|
content: CustomTooltip
|
|
26319
26327
|
}
|
|
26320
26328
|
),
|
|
26321
|
-
chartData.
|
|
26329
|
+
chartData.lastSetTarget > 0 && /* @__PURE__ */ jsx(
|
|
26322
26330
|
ReferenceLine,
|
|
26323
26331
|
{
|
|
26324
|
-
y: chartData.
|
|
26332
|
+
y: chartData.lastSetTarget,
|
|
26325
26333
|
stroke: "#E34329",
|
|
26326
26334
|
strokeDasharray: "5 5",
|
|
26327
26335
|
strokeWidth: 2
|
|
@@ -26379,11 +26387,11 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26379
26387
|
]
|
|
26380
26388
|
}
|
|
26381
26389
|
) }) }),
|
|
26382
|
-
/* @__PURE__ */ jsx("div", { className: "flex justify-center items-center gap-6 mt-3", children: chartData.
|
|
26390
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center items-center gap-6 mt-3", children: chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
26383
26391
|
/* @__PURE__ */ jsx("div", { className: "w-12 h-0.5 border-t-2 border-dashed", style: { borderColor: "#E34329" } }),
|
|
26384
26392
|
/* @__PURE__ */ jsxs("span", { className: "text-xs text-gray-600", children: [
|
|
26385
26393
|
"Target: ",
|
|
26386
|
-
Math.round(chartData.
|
|
26394
|
+
Math.round(chartData.lastSetTarget),
|
|
26387
26395
|
" units/day"
|
|
26388
26396
|
] })
|
|
26389
26397
|
] }) })
|
|
@@ -27229,6 +27237,36 @@ var TimePickerDropdown = ({
|
|
|
27229
27237
|
] })
|
|
27230
27238
|
] });
|
|
27231
27239
|
};
|
|
27240
|
+
var ERROR_MAPPING = {
|
|
27241
|
+
1: {
|
|
27242
|
+
// MEDIA_ERR_ABORTED
|
|
27243
|
+
code: 1,
|
|
27244
|
+
type: "recoverable" /* RECOVERABLE */,
|
|
27245
|
+
message: "Video loading was interrupted",
|
|
27246
|
+
canRetry: true
|
|
27247
|
+
},
|
|
27248
|
+
2: {
|
|
27249
|
+
// MEDIA_ERR_NETWORK
|
|
27250
|
+
code: 2,
|
|
27251
|
+
type: "recoverable" /* RECOVERABLE */,
|
|
27252
|
+
message: "Network error - please check your internet connection",
|
|
27253
|
+
canRetry: true
|
|
27254
|
+
},
|
|
27255
|
+
3: {
|
|
27256
|
+
// MEDIA_ERR_DECODE
|
|
27257
|
+
code: 3,
|
|
27258
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27259
|
+
message: "Stream corrupted due to internet connection",
|
|
27260
|
+
canRetry: false
|
|
27261
|
+
},
|
|
27262
|
+
4: {
|
|
27263
|
+
// MEDIA_ERR_SRC_NOT_SUPPORTED
|
|
27264
|
+
code: 4,
|
|
27265
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27266
|
+
message: "Video format not supported by your browser. Please use Google Chrome.",
|
|
27267
|
+
canRetry: false
|
|
27268
|
+
}
|
|
27269
|
+
};
|
|
27232
27270
|
var videoPlayerStyles = `
|
|
27233
27271
|
.video-player-container {
|
|
27234
27272
|
width: 100%;
|
|
@@ -27459,8 +27497,21 @@ var VideoPlayer = React21__default.forwardRef(({
|
|
|
27459
27497
|
player.on("seeked", () => onSeeked?.(player));
|
|
27460
27498
|
player.on("error", () => {
|
|
27461
27499
|
const error = player.error();
|
|
27462
|
-
|
|
27463
|
-
|
|
27500
|
+
const errorCode = error?.code ?? 0;
|
|
27501
|
+
const errorInfo = ERROR_MAPPING[errorCode] || {
|
|
27502
|
+
code: errorCode || 0,
|
|
27503
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27504
|
+
message: "Unknown playback error occurred",
|
|
27505
|
+
canRetry: false
|
|
27506
|
+
};
|
|
27507
|
+
console.error("[VideoPlayer] Video.js error:", {
|
|
27508
|
+
code: errorCode,
|
|
27509
|
+
type: errorInfo.type,
|
|
27510
|
+
message: errorInfo.message,
|
|
27511
|
+
canRetry: errorInfo.canRetry,
|
|
27512
|
+
originalError: error
|
|
27513
|
+
});
|
|
27514
|
+
onError?.(player, errorInfo);
|
|
27464
27515
|
});
|
|
27465
27516
|
if (src) {
|
|
27466
27517
|
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
@@ -28638,7 +28689,7 @@ var FileManagerFilters = ({
|
|
|
28638
28689
|
];
|
|
28639
28690
|
percentileCategories.forEach((category) => {
|
|
28640
28691
|
if (category.count > 0 && shouldShowCategory(category.id)) {
|
|
28641
|
-
tree.
|
|
28692
|
+
tree.push(category);
|
|
28642
28693
|
}
|
|
28643
28694
|
});
|
|
28644
28695
|
return tree;
|
|
@@ -29193,7 +29244,8 @@ var BottlenecksContent = ({
|
|
|
29193
29244
|
workspaceName,
|
|
29194
29245
|
date,
|
|
29195
29246
|
shift,
|
|
29196
|
-
className
|
|
29247
|
+
className,
|
|
29248
|
+
totalOutput
|
|
29197
29249
|
}) => {
|
|
29198
29250
|
const dashboardConfig = useDashboardConfig();
|
|
29199
29251
|
const timezone = useAppTimezone();
|
|
@@ -29292,8 +29344,9 @@ var BottlenecksContent = ({
|
|
|
29292
29344
|
} = useClipTypesWithCounts(
|
|
29293
29345
|
workspaceId,
|
|
29294
29346
|
date || getOperationalDate(timezone),
|
|
29295
|
-
effectiveShift
|
|
29347
|
+
effectiveShift,
|
|
29296
29348
|
// Use same shift as video loading for consistency
|
|
29349
|
+
totalOutput
|
|
29297
29350
|
);
|
|
29298
29351
|
console.log("[BottlenecksContent] Clip types data:", {
|
|
29299
29352
|
clipTypes,
|
|
@@ -29338,8 +29391,9 @@ var BottlenecksContent = ({
|
|
|
29338
29391
|
const fullResult = await s3ClipsService.getClipCounts(
|
|
29339
29392
|
workspaceId,
|
|
29340
29393
|
operationalDate,
|
|
29341
|
-
shiftStr
|
|
29342
|
-
|
|
29394
|
+
shiftStr,
|
|
29395
|
+
totalOutput
|
|
29396
|
+
// Pass totalOutput for cycle completion adjustment
|
|
29343
29397
|
);
|
|
29344
29398
|
console.log(`[BottlenecksContent] Direct fetch result:`, fullResult);
|
|
29345
29399
|
if (fullResult) {
|
|
@@ -29352,7 +29406,12 @@ var BottlenecksContent = ({
|
|
|
29352
29406
|
} catch (err) {
|
|
29353
29407
|
console.error("[BottlenecksContent] Error fetching clip counts:", err);
|
|
29354
29408
|
if (isMountedRef.current) {
|
|
29355
|
-
setError(
|
|
29409
|
+
setError({
|
|
29410
|
+
type: "fatal",
|
|
29411
|
+
message: "Failed to load clip counts. Please try again.",
|
|
29412
|
+
canSkip: false,
|
|
29413
|
+
canRetry: true
|
|
29414
|
+
});
|
|
29356
29415
|
setIsLoading(false);
|
|
29357
29416
|
}
|
|
29358
29417
|
} finally {
|
|
@@ -29436,7 +29495,12 @@ var BottlenecksContent = ({
|
|
|
29436
29495
|
} catch (err) {
|
|
29437
29496
|
console.error("Error loading first video for category:", err);
|
|
29438
29497
|
if (isMountedRef.current) {
|
|
29439
|
-
setError(
|
|
29498
|
+
setError({
|
|
29499
|
+
type: "fatal",
|
|
29500
|
+
message: "Failed to load clips. Please try again.",
|
|
29501
|
+
canSkip: false,
|
|
29502
|
+
canRetry: true
|
|
29503
|
+
});
|
|
29440
29504
|
setIsCategoryLoading(false);
|
|
29441
29505
|
}
|
|
29442
29506
|
} finally {
|
|
@@ -29718,7 +29782,12 @@ var BottlenecksContent = ({
|
|
|
29718
29782
|
} catch (error2) {
|
|
29719
29783
|
console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
|
|
29720
29784
|
if (isMountedRef.current) {
|
|
29721
|
-
setError(
|
|
29785
|
+
setError({
|
|
29786
|
+
type: "fatal",
|
|
29787
|
+
message: "Failed to load selected clip. Please try again.",
|
|
29788
|
+
canSkip: true,
|
|
29789
|
+
canRetry: true
|
|
29790
|
+
});
|
|
29722
29791
|
clearLoadingState();
|
|
29723
29792
|
}
|
|
29724
29793
|
}
|
|
@@ -29741,7 +29810,12 @@ var BottlenecksContent = ({
|
|
|
29741
29810
|
}
|
|
29742
29811
|
} catch (error2) {
|
|
29743
29812
|
console.error(`[BottlenecksContent] Error in legacy loadAndPlayClip:`, error2);
|
|
29744
|
-
setError(
|
|
29813
|
+
setError({
|
|
29814
|
+
type: "fatal",
|
|
29815
|
+
message: "Failed to load selected clip. Please try again.",
|
|
29816
|
+
canSkip: true,
|
|
29817
|
+
canRetry: true
|
|
29818
|
+
});
|
|
29745
29819
|
setIsNavigating(false);
|
|
29746
29820
|
}
|
|
29747
29821
|
}, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById]);
|
|
@@ -29781,7 +29855,12 @@ var BottlenecksContent = ({
|
|
|
29781
29855
|
}
|
|
29782
29856
|
} catch (error2) {
|
|
29783
29857
|
console.error(`[handleNext] Error navigating:`, error2);
|
|
29784
|
-
setError(
|
|
29858
|
+
setError({
|
|
29859
|
+
type: "fatal",
|
|
29860
|
+
message: "Failed to navigate to next clip",
|
|
29861
|
+
canSkip: true,
|
|
29862
|
+
canRetry: true
|
|
29863
|
+
});
|
|
29785
29864
|
clearLoadingState();
|
|
29786
29865
|
}
|
|
29787
29866
|
}, [clearLoadingState, s3ClipsService]);
|
|
@@ -29817,7 +29896,12 @@ var BottlenecksContent = ({
|
|
|
29817
29896
|
}
|
|
29818
29897
|
} catch (error2) {
|
|
29819
29898
|
console.error(`[handlePrevious] Error navigating:`, error2);
|
|
29820
|
-
setError(
|
|
29899
|
+
setError({
|
|
29900
|
+
type: "fatal",
|
|
29901
|
+
message: "Failed to navigate to previous clip",
|
|
29902
|
+
canSkip: true,
|
|
29903
|
+
canRetry: true
|
|
29904
|
+
});
|
|
29821
29905
|
clearLoadingState();
|
|
29822
29906
|
}
|
|
29823
29907
|
}, [clearLoadingState, s3ClipsService]);
|
|
@@ -29830,7 +29914,7 @@ var BottlenecksContent = ({
|
|
|
29830
29914
|
const handleVideoReady = useCallback((player) => {
|
|
29831
29915
|
console.log("Video.js player ready - NOT clearing loading (wait for playing event)");
|
|
29832
29916
|
videoRetryCountRef.current = 0;
|
|
29833
|
-
if (error?.
|
|
29917
|
+
if (error?.isRetrying) {
|
|
29834
29918
|
setError(null);
|
|
29835
29919
|
}
|
|
29836
29920
|
}, [error]);
|
|
@@ -29875,20 +29959,53 @@ var BottlenecksContent = ({
|
|
|
29875
29959
|
}, [clearLoadingState]);
|
|
29876
29960
|
const handleVideoLoadingChange = useCallback((isLoading2) => {
|
|
29877
29961
|
console.log(`[BottlenecksContent] Video loading state changed: ${isLoading2}`);
|
|
29962
|
+
if (error && error.type === "fatal") {
|
|
29963
|
+
console.log(`[BottlenecksContent] Ignoring loading state change - fatal error is showing`);
|
|
29964
|
+
return;
|
|
29965
|
+
}
|
|
29878
29966
|
setIsVideoBuffering(isLoading2);
|
|
29879
|
-
}, []);
|
|
29967
|
+
}, [error]);
|
|
29880
29968
|
const handleVideoEnded = useCallback((player) => {
|
|
29881
29969
|
handleNext();
|
|
29882
29970
|
}, [handleNext]);
|
|
29883
29971
|
const videoRetryCountRef = useRef(0);
|
|
29884
|
-
const handleVideoError = useCallback((player,
|
|
29885
|
-
console.error("Video.js error:",
|
|
29972
|
+
const handleVideoError = useCallback((player, errorInfo) => {
|
|
29973
|
+
console.error("[BottlenecksContent] Video.js error:", errorInfo);
|
|
29886
29974
|
setIsPlaying(false);
|
|
29975
|
+
setIsVideoBuffering(false);
|
|
29976
|
+
const errorCode = errorInfo?.code || 0;
|
|
29977
|
+
const canRetry = errorInfo?.canRetry ?? false;
|
|
29978
|
+
const errorMessage = errorInfo?.message || "Unknown error";
|
|
29979
|
+
console.log(`[Video Error] Code: ${errorCode}, Can Retry: ${canRetry}, Message: ${errorMessage}`);
|
|
29980
|
+
if (!canRetry) {
|
|
29981
|
+
console.log("[Video Error] Non-recoverable error - showing error overlay immediately");
|
|
29982
|
+
setError({
|
|
29983
|
+
type: "fatal",
|
|
29984
|
+
code: errorCode,
|
|
29985
|
+
message: errorMessage,
|
|
29986
|
+
canSkip: true,
|
|
29987
|
+
canRetry: false
|
|
29988
|
+
});
|
|
29989
|
+
clearLoadingState();
|
|
29990
|
+
videoRetryCountRef.current = 0;
|
|
29991
|
+
trackCoreEvent("clips_video_error_non_recoverable", {
|
|
29992
|
+
workspaceId,
|
|
29993
|
+
category: activeFilterRef.current,
|
|
29994
|
+
videoId: currentVideo?.id,
|
|
29995
|
+
errorCode,
|
|
29996
|
+
errorMessage
|
|
29997
|
+
});
|
|
29998
|
+
return;
|
|
29999
|
+
}
|
|
29887
30000
|
if (videoRetryCountRef.current < 3 && currentVideo) {
|
|
29888
30001
|
videoRetryCountRef.current++;
|
|
29889
30002
|
const retryDelay = 1e3 * videoRetryCountRef.current;
|
|
29890
|
-
console.log(`[Video Error] Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
|
|
29891
|
-
setError(
|
|
30003
|
+
console.log(`[Video Error] Recoverable error - Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
|
|
30004
|
+
setError({
|
|
30005
|
+
type: "retrying",
|
|
30006
|
+
message: `Retrying... (${videoRetryCountRef.current}/3)`,
|
|
30007
|
+
isRetrying: true
|
|
30008
|
+
});
|
|
29892
30009
|
setTimeout(() => {
|
|
29893
30010
|
if (videoRef.current && currentVideo && isMountedRef.current) {
|
|
29894
30011
|
setError(null);
|
|
@@ -29896,16 +30013,26 @@ var BottlenecksContent = ({
|
|
|
29896
30013
|
}
|
|
29897
30014
|
}, retryDelay);
|
|
29898
30015
|
} else {
|
|
29899
|
-
|
|
30016
|
+
console.log("[Video Error] Retries exhausted - showing final error overlay");
|
|
30017
|
+
setError({
|
|
30018
|
+
type: "fatal",
|
|
30019
|
+
code: errorCode,
|
|
30020
|
+
message: errorMessage,
|
|
30021
|
+
canSkip: true,
|
|
30022
|
+
canRetry: true
|
|
30023
|
+
// Allow manual retry for network errors
|
|
30024
|
+
});
|
|
29900
30025
|
videoRetryCountRef.current = 0;
|
|
29901
30026
|
trackCoreEvent("clips_video_error_final", {
|
|
29902
30027
|
workspaceId,
|
|
29903
30028
|
category: activeFilterRef.current,
|
|
29904
30029
|
videoId: currentVideo?.id,
|
|
29905
|
-
|
|
30030
|
+
errorCode,
|
|
30031
|
+
errorMessage,
|
|
30032
|
+
attempts: 3
|
|
29906
30033
|
});
|
|
29907
30034
|
}
|
|
29908
|
-
}, [currentVideo, workspaceId]);
|
|
30035
|
+
}, [currentVideo, workspaceId, clearLoadingState]);
|
|
29909
30036
|
useEffect(() => {
|
|
29910
30037
|
isMountedRef.current = true;
|
|
29911
30038
|
return () => {
|
|
@@ -29923,9 +30050,13 @@ var BottlenecksContent = ({
|
|
|
29923
30050
|
}, [s3ClipsService]);
|
|
29924
30051
|
useEffect(() => {
|
|
29925
30052
|
if (filteredVideos.length > 0 && currentIndex < filteredVideos.length) {
|
|
30053
|
+
if (error && error.type === "fatal") {
|
|
30054
|
+
console.log("[BottlenecksContent] Not clearing fatal error on video change - let user handle it");
|
|
30055
|
+
return;
|
|
30056
|
+
}
|
|
29926
30057
|
setError(null);
|
|
29927
30058
|
}
|
|
29928
|
-
}, [currentIndex, filteredVideos]);
|
|
30059
|
+
}, [currentIndex, filteredVideos, error]);
|
|
29929
30060
|
useEffect(() => {
|
|
29930
30061
|
if (!isTransitioning && pendingVideo) {
|
|
29931
30062
|
const timer = setTimeout(() => {
|
|
@@ -30000,11 +30131,11 @@ var BottlenecksContent = ({
|
|
|
30000
30131
|
if ((isLoading || clipTypesLoading) && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
|
|
30001
30132
|
return /* @__PURE__ */ jsx("div", { className: "flex-grow p-4 flex items-center justify-center h-[calc(100vh-12rem)]", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "lg", message: "Loading clips..." }) });
|
|
30002
30133
|
}
|
|
30003
|
-
if (error || clipTypesError) {
|
|
30134
|
+
if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
|
|
30004
30135
|
return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
|
|
30005
30136
|
/* @__PURE__ */ jsx(XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
|
|
30006
30137
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "Error Loading Clips" }),
|
|
30007
|
-
/* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error || clipTypesError })
|
|
30138
|
+
/* @__PURE__ */ jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
|
|
30008
30139
|
] });
|
|
30009
30140
|
}
|
|
30010
30141
|
const categoriesToShow = clipTypes.length > 0 ? clipTypes : [];
|
|
@@ -30051,7 +30182,7 @@ var BottlenecksContent = ({
|
|
|
30051
30182
|
),
|
|
30052
30183
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
30053
30184
|
/* @__PURE__ */ jsx("span", { className: "text-sm px-2 py-1 bg-blue-50 text-blue-700 rounded-full font-medium tabular-nums", children: categoryMetadata.length > 0 ? `${currentMetadataIndex + 1} / ${categoryMetadata.length}` : "0 / 0" }),
|
|
30054
|
-
error && /* @__PURE__ */ jsx("span", { className: "text-xs text-
|
|
30185
|
+
error && error.type === "retrying" && /* @__PURE__ */ jsx("span", { className: "text-xs text-orange-600 font-medium", children: error.message })
|
|
30055
30186
|
] }),
|
|
30056
30187
|
/* @__PURE__ */ jsx(
|
|
30057
30188
|
"button",
|
|
@@ -30065,7 +30196,6 @@ var BottlenecksContent = ({
|
|
|
30065
30196
|
)
|
|
30066
30197
|
] })
|
|
30067
30198
|
] }) }),
|
|
30068
|
-
/* Show video if we have filtered videos and current video, or show loading */
|
|
30069
30199
|
filteredVideos.length > 0 && currentVideo ? /* @__PURE__ */ jsx("div", { className: "p-4 h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx("div", { className: "relative h-full group", children: /* @__PURE__ */ jsxs("div", { className: "relative w-full h-full overflow-hidden rounded-md shadow-inner bg-gray-900", children: [
|
|
30070
30200
|
/* @__PURE__ */ jsx(
|
|
30071
30201
|
CroppedVideoPlayer,
|
|
@@ -30097,30 +30227,44 @@ var BottlenecksContent = ({
|
|
|
30097
30227
|
}
|
|
30098
30228
|
}
|
|
30099
30229
|
),
|
|
30100
|
-
(isTransitioning || isVideoBuffering && isInitialLoading) && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
30101
|
-
!isTransitioning && isVideoBuffering && !isInitialLoading && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
30102
|
-
error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/
|
|
30230
|
+
(isTransitioning || isVideoBuffering && isInitialLoading) && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
30231
|
+
!isTransitioning && isVideoBuffering && !isInitialLoading && !error && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-30 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md", message: "Loading video..." }) }),
|
|
30232
|
+
error && error.type === "retrying" && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-40 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
30233
|
+
/* @__PURE__ */ jsx(OptifyeLogoLoader_default, { size: "md" }),
|
|
30234
|
+
/* @__PURE__ */ jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
|
|
30235
|
+
] }) }),
|
|
30236
|
+
error && error.type === "fatal" && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-50 flex items-center justify-center bg-black/90 text-white p-4", children: /* @__PURE__ */ jsxs("div", { className: "text-center max-w-md", children: [
|
|
30103
30237
|
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 mx-auto mb-4 text-red-400", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 16.5c-.77.833.192 2.5 1.732 2.5z" }) }),
|
|
30104
|
-
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children: "
|
|
30105
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-300 mb-
|
|
30106
|
-
/* @__PURE__ */ jsxs("
|
|
30107
|
-
/* @__PURE__ */ jsx(
|
|
30108
|
-
|
|
30109
|
-
|
|
30110
|
-
|
|
30111
|
-
|
|
30112
|
-
|
|
30113
|
-
|
|
30114
|
-
|
|
30115
|
-
|
|
30116
|
-
|
|
30117
|
-
|
|
30118
|
-
|
|
30119
|
-
|
|
30120
|
-
|
|
30121
|
-
|
|
30122
|
-
|
|
30123
|
-
|
|
30238
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-2", children: error.code === 3 ? "Stream Corrupted" : error.code === 4 ? "Format Not Supported" : error.code === 2 ? "Network Error" : error.code === 1 ? "Loading Interrupted" : "Playback Error" }),
|
|
30239
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
|
|
30240
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3 justify-center", children: [
|
|
30241
|
+
error.canSkip && /* @__PURE__ */ jsx(
|
|
30242
|
+
"button",
|
|
30243
|
+
{
|
|
30244
|
+
onClick: () => {
|
|
30245
|
+
setError(null);
|
|
30246
|
+
videoRetryCountRef.current = 0;
|
|
30247
|
+
handleNext();
|
|
30248
|
+
},
|
|
30249
|
+
className: "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 rounded-md text-sm font-medium transition-colors",
|
|
30250
|
+
children: "Skip to Next Clip"
|
|
30251
|
+
}
|
|
30252
|
+
),
|
|
30253
|
+
error.canRetry && /* @__PURE__ */ jsx(
|
|
30254
|
+
"button",
|
|
30255
|
+
{
|
|
30256
|
+
onClick: () => {
|
|
30257
|
+
setError(null);
|
|
30258
|
+
videoRetryCountRef.current = 0;
|
|
30259
|
+
if (videoRef.current) {
|
|
30260
|
+
videoRef.current.dispose();
|
|
30261
|
+
}
|
|
30262
|
+
},
|
|
30263
|
+
className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
|
|
30264
|
+
children: "Retry"
|
|
30265
|
+
}
|
|
30266
|
+
)
|
|
30267
|
+
] })
|
|
30124
30268
|
] }) }),
|
|
30125
30269
|
(currentVideo.type === "cycle_completion" || currentVideo.type === "bottleneck" && currentVideo.description.toLowerCase().includes("cycle time")) && currentVideo.cycle_time_seconds || currentVideo.type === "idle_time" || currentVideo.type === "low_value" ? /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10 bg-black/60 backdrop-blur-sm px-3 py-1.5 rounded-lg text-white shadow-lg text-xs", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
30126
30270
|
/* @__PURE__ */ jsx("div", { className: `flex-shrink-0 h-2.5 w-2.5 rounded-full ${currentVideo.type === "low_value" || currentVideo.type === "idle_time" ? "bg-purple-400" : isPercentileCategory(activeFilterRef.current) ? activeFilterRef.current === "fast-cycles" ? "bg-green-600" : activeFilterRef.current === "slow-cycles" ? "bg-red-700" : "bg-orange-500" : currentVideo.type === "cycle_completion" ? "bg-blue-600" : "bg-gray-500"} mr-2 animate-pulse` }),
|
|
@@ -42629,7 +42773,7 @@ var WorkspaceDetailView = ({
|
|
|
42629
42773
|
/* @__PURE__ */ jsxs(Card2, { children: [
|
|
42630
42774
|
/* @__PURE__ */ jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
42631
42775
|
/* @__PURE__ */ jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
42632
|
-
/* @__PURE__ */ jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42776
|
+
/* @__PURE__ */ jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42633
42777
|
/* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
42634
42778
|
"Standard: ",
|
|
42635
42779
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -42749,7 +42893,7 @@ var WorkspaceDetailView = ({
|
|
|
42749
42893
|
/* @__PURE__ */ jsxs(Card2, { children: [
|
|
42750
42894
|
/* @__PURE__ */ jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
42751
42895
|
/* @__PURE__ */ jsx(CardContent2, { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
42752
|
-
/* @__PURE__ */ jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42896
|
+
/* @__PURE__ */ jsx("p", { className: `text-5xl font-bold ${workspace.avg_cycle_time > (workspace.ideal_cycle_time || 0) ? "text-red-500" : "text-green-500"}`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42753
42897
|
/* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
42754
42898
|
"Standard: ",
|
|
42755
42899
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -42824,6 +42968,7 @@ var WorkspaceDetailView = ({
|
|
|
42824
42968
|
workspaceName: formattedWorkspaceName,
|
|
42825
42969
|
date,
|
|
42826
42970
|
shift,
|
|
42971
|
+
totalOutput: workspace?.total_actions,
|
|
42827
42972
|
className: "h-[calc(100vh-10rem)]"
|
|
42828
42973
|
}
|
|
42829
42974
|
) })
|