@optifye/dashboard-core 6.6.10 → 6.6.12
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 +40 -13
- package/dist/index.d.mts +34 -2
- package/dist/index.d.ts +34 -2
- package/dist/index.js +591 -299
- package/dist/index.mjs +592 -301
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4044,12 +4044,8 @@ var S3ClipsSupabaseService = class {
|
|
|
4044
4044
|
id: clip.clip_id,
|
|
4045
4045
|
src: clip.playlist,
|
|
4046
4046
|
// Raw playlist content
|
|
4047
|
-
timestamp:
|
|
4048
|
-
|
|
4049
|
-
hour: "2-digit",
|
|
4050
|
-
minute: "2-digit",
|
|
4051
|
-
second: "2-digit"
|
|
4052
|
-
}),
|
|
4047
|
+
timestamp: clip.timestamp,
|
|
4048
|
+
// Use pre-formatted timestamp from API (already in 12-hour format with seconds)
|
|
4053
4049
|
severity: this.getSeverityFromClipType(clip.clip_type_name),
|
|
4054
4050
|
description: this.getDescriptionFromClipType(clip.clip_type_name),
|
|
4055
4051
|
type: clip.clip_type_name,
|
|
@@ -6545,6 +6541,10 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6545
6541
|
const lineIdRef = React21.useRef(lineId);
|
|
6546
6542
|
const isFetchingRef = React21.useRef(false);
|
|
6547
6543
|
const updateQueueRef = React21.useRef(false);
|
|
6544
|
+
const onLineMetricsUpdateRef = React21.useRef(onLineMetricsUpdate);
|
|
6545
|
+
React21.useEffect(() => {
|
|
6546
|
+
onLineMetricsUpdateRef.current = onLineMetricsUpdate;
|
|
6547
|
+
}, [onLineMetricsUpdate]);
|
|
6548
6548
|
const companySpecificMetricsTable = React21.useMemo(
|
|
6549
6549
|
() => getCompanyMetricsTableName(entityConfig.companyId, "performance_metrics"),
|
|
6550
6550
|
[entityConfig.companyId]
|
|
@@ -6660,13 +6660,17 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6660
6660
|
defaultTimezone,
|
|
6661
6661
|
shiftConfig
|
|
6662
6662
|
]);
|
|
6663
|
+
const fetchAllMetricsRef = React21.useRef(fetchAllMetrics);
|
|
6664
|
+
React21.useEffect(() => {
|
|
6665
|
+
fetchAllMetricsRef.current = fetchAllMetrics;
|
|
6666
|
+
}, [fetchAllMetrics]);
|
|
6663
6667
|
const queueUpdate = React21.useCallback(() => {
|
|
6664
6668
|
if (updateQueueRef.current || !supabase) {
|
|
6665
6669
|
return;
|
|
6666
6670
|
}
|
|
6667
6671
|
updateQueueRef.current = true;
|
|
6668
|
-
|
|
6669
|
-
}, [
|
|
6672
|
+
fetchAllMetricsRef.current();
|
|
6673
|
+
}, [supabase]);
|
|
6670
6674
|
React21.useEffect(() => {
|
|
6671
6675
|
if (lineId && supabase) {
|
|
6672
6676
|
fetchAllMetrics();
|
|
@@ -6681,11 +6685,11 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6681
6685
|
const operationalDateForSubscription = getOperationalDate(defaultTimezone);
|
|
6682
6686
|
const targetLineIds = currentLineIdToUse === (entityConfig.factoryViewId || "factory") ? getConfiguredLineIds(entityConfig) : [currentLineIdToUse];
|
|
6683
6687
|
if (targetLineIds.length === 0) return;
|
|
6684
|
-
const wsMetricsFilter = `
|
|
6685
|
-
const lineMetricsFilter = `
|
|
6688
|
+
const wsMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
6689
|
+
const lineMetricsFilter = `line_id=in.(${targetLineIds.map((id3) => `"${id3}"`).join(",")})`;
|
|
6686
6690
|
const channels = [];
|
|
6687
6691
|
const createSubscription = (table, filter2, channelNameBase, callback) => {
|
|
6688
|
-
const channelName = `${channelNameBase}-${
|
|
6692
|
+
const channelName = `${channelNameBase}-${Date.now()}`.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
6689
6693
|
const channel = supabase.channel(channelName).on(
|
|
6690
6694
|
"postgres_changes",
|
|
6691
6695
|
{ event: "*", schema, table, filter: filter2 },
|
|
@@ -6695,40 +6699,29 @@ var useDashboardMetrics = ({ onLineMetricsUpdate, lineId }) => {
|
|
|
6695
6699
|
callback();
|
|
6696
6700
|
}
|
|
6697
6701
|
}
|
|
6698
|
-
).subscribe(
|
|
6699
|
-
if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.CHANNEL_ERROR && err) {
|
|
6700
|
-
console.error(`[useDashboardMetrics] Subscription error for ${table} on ${currentLineIdToUse}:`, err.message);
|
|
6701
|
-
} else if (status === supabaseJs.REALTIME_SUBSCRIBE_STATES.TIMED_OUT) {
|
|
6702
|
-
console.warn(`[useDashboardMetrics] Subscription timeout for ${table} on ${currentLineIdToUse}.`);
|
|
6703
|
-
}
|
|
6704
|
-
});
|
|
6702
|
+
).subscribe();
|
|
6705
6703
|
channels.push(channel);
|
|
6706
6704
|
};
|
|
6707
6705
|
createSubscription(companySpecificMetricsTable, wsMetricsFilter, "dashboard-ws-metrics", queueUpdate);
|
|
6708
6706
|
createSubscription(configuredLineMetricsTable, lineMetricsFilter, "dashboard-line-metrics", () => {
|
|
6709
6707
|
queueUpdate();
|
|
6710
|
-
|
|
6708
|
+
onLineMetricsUpdateRef.current?.();
|
|
6711
6709
|
});
|
|
6712
6710
|
return () => {
|
|
6713
6711
|
channels.forEach((channel) => {
|
|
6714
|
-
supabase?.removeChannel(channel)
|
|
6712
|
+
supabase?.removeChannel(channel);
|
|
6715
6713
|
});
|
|
6716
6714
|
};
|
|
6717
6715
|
}, [
|
|
6718
6716
|
supabase,
|
|
6719
|
-
// fetchAllMetrics, // fetchAllMetrics is now a dependency of queueUpdate
|
|
6720
6717
|
queueUpdate,
|
|
6721
|
-
// Section 6: Add queueUpdate as dependency
|
|
6722
6718
|
companySpecificMetricsTable,
|
|
6723
6719
|
configuredLineMetricsTable,
|
|
6724
6720
|
schema,
|
|
6725
|
-
entityConfig,
|
|
6726
|
-
|
|
6721
|
+
entityConfig?.companyId,
|
|
6722
|
+
entityConfig?.factoryViewId,
|
|
6727
6723
|
defaultTimezone,
|
|
6728
|
-
shiftConfig,
|
|
6729
|
-
onLineMetricsUpdate,
|
|
6730
6724
|
lineId
|
|
6731
|
-
// Add lineId from props to re-run effect if it changes, managed by lineIdRef inside effect
|
|
6732
6725
|
]);
|
|
6733
6726
|
return {
|
|
6734
6727
|
workspaceMetrics: metrics2?.workspaceMetrics || [],
|
|
@@ -6889,6 +6882,7 @@ var useRealtimeLineMetrics = ({
|
|
|
6889
6882
|
const updateQueueRef = React21.useRef(false);
|
|
6890
6883
|
const isFetchingRef = React21.useRef(false);
|
|
6891
6884
|
const channelsRef = React21.useRef([]);
|
|
6885
|
+
const fetchTimeoutRef = React21.useRef(null);
|
|
6892
6886
|
const currentShift = React21.useMemo(() => getCurrentShift(dateTimeConfig.defaultTimezone || "Asia/Kolkata", shiftConfig), [dateTimeConfig.defaultTimezone, shiftConfig]);
|
|
6893
6887
|
const shiftId = React21.useMemo(
|
|
6894
6888
|
() => urlShiftId !== void 0 ? urlShiftId : currentShift.shiftId,
|
|
@@ -7098,9 +7092,17 @@ var useRealtimeLineMetrics = ({
|
|
|
7098
7092
|
}
|
|
7099
7093
|
}, [supabase, date, shiftId, urlShiftId, onMetricsUpdate, entityConfig, dateTimeConfig.defaultTimezone]);
|
|
7100
7094
|
const queueUpdate = React21.useCallback(() => {
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
|
|
7095
|
+
console.log("[useRealtimeLineMetrics] Update queued, debouncing...");
|
|
7096
|
+
if (fetchTimeoutRef.current) {
|
|
7097
|
+
clearTimeout(fetchTimeoutRef.current);
|
|
7098
|
+
}
|
|
7099
|
+
fetchTimeoutRef.current = setTimeout(() => {
|
|
7100
|
+
if (updateQueueRef.current) return;
|
|
7101
|
+
updateQueueRef.current = true;
|
|
7102
|
+
console.log("[useRealtimeLineMetrics] Debounced fetch triggered");
|
|
7103
|
+
fetchData();
|
|
7104
|
+
fetchTimeoutRef.current = null;
|
|
7105
|
+
}, 500);
|
|
7104
7106
|
}, [fetchData]);
|
|
7105
7107
|
const setupSubscriptions = React21.useCallback(() => {
|
|
7106
7108
|
if (channelsRef.current.length > 0) {
|
|
@@ -7178,6 +7180,10 @@ var useRealtimeLineMetrics = ({
|
|
|
7178
7180
|
}
|
|
7179
7181
|
setupSubscriptions();
|
|
7180
7182
|
return () => {
|
|
7183
|
+
if (fetchTimeoutRef.current) {
|
|
7184
|
+
clearTimeout(fetchTimeoutRef.current);
|
|
7185
|
+
fetchTimeoutRef.current = null;
|
|
7186
|
+
}
|
|
7181
7187
|
if (channelsRef.current.length > 0) {
|
|
7182
7188
|
channelsRef.current.forEach((channel) => {
|
|
7183
7189
|
if (process.env.NODE_ENV === "development") {
|
|
@@ -8124,13 +8130,10 @@ var getWorkspaceDisplayName = (workspaceId, lineId) => {
|
|
|
8124
8130
|
}
|
|
8125
8131
|
}
|
|
8126
8132
|
if (displayName) {
|
|
8127
|
-
console.log(`getWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${displayName} (from Supabase)`);
|
|
8128
8133
|
return displayName;
|
|
8129
8134
|
} else {
|
|
8130
8135
|
if (isInitialized) {
|
|
8131
|
-
console.
|
|
8132
|
-
} else {
|
|
8133
|
-
console.log(`getWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (Supabase not initialized yet)`);
|
|
8136
|
+
console.warn(`getWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (not found in Supabase data)`);
|
|
8134
8137
|
}
|
|
8135
8138
|
return workspaceId;
|
|
8136
8139
|
}
|
|
@@ -8167,13 +8170,10 @@ var getShortWorkspaceDisplayName = (workspaceId, lineId) => {
|
|
|
8167
8170
|
}
|
|
8168
8171
|
}
|
|
8169
8172
|
if (displayName) {
|
|
8170
|
-
console.log(`getShortWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${displayName} (from Supabase)`);
|
|
8171
8173
|
return displayName;
|
|
8172
8174
|
} else {
|
|
8173
8175
|
if (isInitialized) {
|
|
8174
|
-
console.
|
|
8175
|
-
} else {
|
|
8176
|
-
console.log(`getShortWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (Supabase not initialized yet)`);
|
|
8176
|
+
console.warn(`getShortWorkspaceDisplayName(${workspaceId}, lineId: ${lineId}) -> ${workspaceId} (not found in Supabase data)`);
|
|
8177
8177
|
}
|
|
8178
8178
|
return workspaceId;
|
|
8179
8179
|
}
|
|
@@ -8479,6 +8479,8 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
8479
8479
|
const [loading, setLoading] = React21.useState(true);
|
|
8480
8480
|
const [error, setError] = React21.useState(null);
|
|
8481
8481
|
const [initialized, setInitialized] = React21.useState(false);
|
|
8482
|
+
const fetchTimeoutRef = React21.useRef(null);
|
|
8483
|
+
const isFetchingRef = React21.useRef(false);
|
|
8482
8484
|
const queryShiftId = React21.useMemo(() => {
|
|
8483
8485
|
const currentShift = getCurrentShift(
|
|
8484
8486
|
dateTimeConfig.defaultTimezone || "Asia/Kolkata",
|
|
@@ -8497,16 +8499,15 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
8497
8499
|
}, [entityConfig.companyId]);
|
|
8498
8500
|
const schema = databaseConfig.schema ?? "public";
|
|
8499
8501
|
const fetchWorkspaceMetrics = React21.useCallback(async () => {
|
|
8502
|
+
if (isFetchingRef.current) {
|
|
8503
|
+
return;
|
|
8504
|
+
}
|
|
8505
|
+
isFetchingRef.current = true;
|
|
8500
8506
|
if (!initialized) {
|
|
8501
8507
|
setLoading(true);
|
|
8502
8508
|
}
|
|
8503
8509
|
setError(null);
|
|
8504
8510
|
try {
|
|
8505
|
-
console.log("Fetching all workspace metrics with params:", {
|
|
8506
|
-
queryDate,
|
|
8507
|
-
queryShiftId,
|
|
8508
|
-
metricsTable
|
|
8509
|
-
});
|
|
8510
8511
|
const configuredLineIds = getConfiguredLineIds(entityConfig);
|
|
8511
8512
|
let enabledWorkspaceIds = [];
|
|
8512
8513
|
for (const lineId of configuredLineIds) {
|
|
@@ -8549,13 +8550,26 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
8549
8550
|
efficiency: item.efficiency || 0,
|
|
8550
8551
|
action_threshold: item.total_day_output || 0
|
|
8551
8552
|
}));
|
|
8552
|
-
setWorkspaces(
|
|
8553
|
+
setWorkspaces((prevWorkspaces) => {
|
|
8554
|
+
if (prevWorkspaces.length !== transformedData.length) {
|
|
8555
|
+
return transformedData;
|
|
8556
|
+
}
|
|
8557
|
+
const hasChanges = transformedData.some((newWs) => {
|
|
8558
|
+
const prevWs = prevWorkspaces.find((w) => w.workspace_uuid === newWs.workspace_uuid);
|
|
8559
|
+
if (!prevWs) {
|
|
8560
|
+
return true;
|
|
8561
|
+
}
|
|
8562
|
+
return prevWs.efficiency !== newWs.efficiency || prevWs.action_count !== newWs.action_count || prevWs.action_threshold !== newWs.action_threshold || prevWs.avg_cycle_time !== newWs.avg_cycle_time || prevWs.workspace_name !== newWs.workspace_name;
|
|
8563
|
+
});
|
|
8564
|
+
return hasChanges ? transformedData : prevWorkspaces;
|
|
8565
|
+
});
|
|
8553
8566
|
setInitialized(true);
|
|
8554
8567
|
} catch (err) {
|
|
8555
|
-
console.error("Error
|
|
8568
|
+
console.error("[useAllWorkspaceMetrics] Error:", err.message);
|
|
8556
8569
|
setError({ message: err.message, code: err.code || "FETCH_ERROR" });
|
|
8557
8570
|
} finally {
|
|
8558
8571
|
setLoading(false);
|
|
8572
|
+
isFetchingRef.current = false;
|
|
8559
8573
|
}
|
|
8560
8574
|
}, [queryDate, queryShiftId, metricsTable, supabase, entityConfig.companyId]);
|
|
8561
8575
|
React21.useEffect(() => {
|
|
@@ -8563,25 +8577,37 @@ var useAllWorkspaceMetrics = (options) => {
|
|
|
8563
8577
|
fetchWorkspaceMetrics();
|
|
8564
8578
|
}
|
|
8565
8579
|
const setupSubscription = () => {
|
|
8566
|
-
const filter2 = `date=eq.${queryDate} AND shift_id=eq.${queryShiftId}`;
|
|
8567
|
-
console.log("Setting up subscription for all workspaces with filter:", filter2);
|
|
8568
8580
|
const channel2 = supabase.channel(`all-workspace-metrics-${Date.now()}`).on(
|
|
8569
8581
|
"postgres_changes",
|
|
8570
8582
|
{
|
|
8571
8583
|
event: "*",
|
|
8572
8584
|
schema,
|
|
8573
|
-
table: metricsTable
|
|
8574
|
-
filter: filter2
|
|
8585
|
+
table: metricsTable
|
|
8575
8586
|
},
|
|
8576
8587
|
async (payload) => {
|
|
8577
|
-
|
|
8578
|
-
|
|
8588
|
+
const data = payload.new || payload.old;
|
|
8589
|
+
if (data?.date !== queryDate || data?.shift_id !== queryShiftId) {
|
|
8590
|
+
return;
|
|
8591
|
+
}
|
|
8592
|
+
if (fetchTimeoutRef.current) {
|
|
8593
|
+
clearTimeout(fetchTimeoutRef.current);
|
|
8594
|
+
}
|
|
8595
|
+
fetchTimeoutRef.current = setTimeout(async () => {
|
|
8596
|
+
if (!isFetchingRef.current) {
|
|
8597
|
+
await fetchWorkspaceMetrics();
|
|
8598
|
+
}
|
|
8599
|
+
fetchTimeoutRef.current = null;
|
|
8600
|
+
}, 300);
|
|
8579
8601
|
}
|
|
8580
8602
|
).subscribe();
|
|
8581
8603
|
return channel2;
|
|
8582
8604
|
};
|
|
8583
8605
|
const channel = setupSubscription();
|
|
8584
8606
|
return () => {
|
|
8607
|
+
if (fetchTimeoutRef.current) {
|
|
8608
|
+
clearTimeout(fetchTimeoutRef.current);
|
|
8609
|
+
fetchTimeoutRef.current = null;
|
|
8610
|
+
}
|
|
8585
8611
|
if (channel) {
|
|
8586
8612
|
supabase.removeChannel(channel);
|
|
8587
8613
|
}
|
|
@@ -21983,7 +22009,16 @@ var VideoCard = React21__namespace.default.memo(({
|
|
|
21983
22009
|
}
|
|
21984
22010
|
);
|
|
21985
22011
|
}, (prevProps, nextProps) => {
|
|
21986
|
-
|
|
22012
|
+
if (prevProps.workspace.efficiency !== nextProps.workspace.efficiency || prevProps.workspace.trend !== nextProps.workspace.trend || prevProps.workspace.performance_score !== nextProps.workspace.performance_score || prevProps.workspace.pph !== nextProps.workspace.pph) {
|
|
22013
|
+
return false;
|
|
22014
|
+
}
|
|
22015
|
+
if (prevProps.hlsUrl !== nextProps.hlsUrl || prevProps.shouldPlay !== nextProps.shouldPlay) {
|
|
22016
|
+
return false;
|
|
22017
|
+
}
|
|
22018
|
+
if (prevProps.cropping?.x !== nextProps.cropping?.x || prevProps.cropping?.y !== nextProps.cropping?.y || prevProps.cropping?.width !== nextProps.cropping?.width || prevProps.cropping?.height !== nextProps.cropping?.height) {
|
|
22019
|
+
return false;
|
|
22020
|
+
}
|
|
22021
|
+
return true;
|
|
21987
22022
|
});
|
|
21988
22023
|
VideoCard.displayName = "VideoCard";
|
|
21989
22024
|
var DEFAULT_HLS_URL = "https://192.168.5.9:8443/cam1.m3u8";
|
|
@@ -22279,7 +22314,7 @@ var WorkspaceMetricCardsImpl = ({
|
|
|
22279
22314
|
/* @__PURE__ */ jsxRuntime.jsxs(Card2, { className: "flex flex-col bg-white shadow-sm h-full min-h-[150px] sm:min-h-0", children: [
|
|
22280
22315
|
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
22281
22316
|
/* @__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: [
|
|
22282
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
22317
|
+
/* @__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) }),
|
|
22283
22318
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
22284
22319
|
"Standard: ",
|
|
22285
22320
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -22991,7 +23026,8 @@ var BreakNotificationPopup = ({
|
|
|
22991
23026
|
onDismiss,
|
|
22992
23027
|
isVisible = true,
|
|
22993
23028
|
className = "",
|
|
22994
|
-
lineNames = {}
|
|
23029
|
+
lineNames = {},
|
|
23030
|
+
axelImagePath = "/axel-profile.png"
|
|
22995
23031
|
}) => {
|
|
22996
23032
|
const [isDismissed, setIsDismissed] = React21.useState(false);
|
|
22997
23033
|
const [currentTime, setCurrentTime] = React21.useState(/* @__PURE__ */ new Date());
|
|
@@ -23016,6 +23052,109 @@ var BreakNotificationPopup = ({
|
|
|
23016
23052
|
if (!isVisible || isDismissed || activeBreaks.length === 0) {
|
|
23017
23053
|
return null;
|
|
23018
23054
|
}
|
|
23055
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: activeBreaks.map((breakItem, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
23056
|
+
motion.div,
|
|
23057
|
+
{
|
|
23058
|
+
initial: { opacity: 0, x: 100, y: -20 },
|
|
23059
|
+
animate: { opacity: 1, x: 0, y: 0 },
|
|
23060
|
+
exit: { opacity: 0, x: 100, y: -20 },
|
|
23061
|
+
transition: { duration: 0.3, ease: "easeOut", delay: index * 0.1 },
|
|
23062
|
+
className: `fixed right-4 z-50 max-w-xs w-full ${className}`,
|
|
23063
|
+
style: { top: `${6 + index * 12}rem` },
|
|
23064
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-lg overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
|
|
23065
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
|
|
23066
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23067
|
+
"img",
|
|
23068
|
+
{
|
|
23069
|
+
src: axelImagePath,
|
|
23070
|
+
alt: "Axel AI",
|
|
23071
|
+
className: "w-10 h-10 rounded-full object-cover border-2 border-gray-200 shadow-sm",
|
|
23072
|
+
onError: (e) => {
|
|
23073
|
+
const target = e.currentTarget;
|
|
23074
|
+
target.style.display = "none";
|
|
23075
|
+
const fallback = document.createElement("div");
|
|
23076
|
+
fallback.className = "w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold text-base shadow-sm border-2 border-gray-200";
|
|
23077
|
+
fallback.textContent = "A";
|
|
23078
|
+
target.parentElement?.appendChild(fallback);
|
|
23079
|
+
}
|
|
23080
|
+
}
|
|
23081
|
+
) }),
|
|
23082
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
23083
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
|
|
23084
|
+
/* @__PURE__ */ jsxRuntime.jsxs("h4", { className: "font-semibold text-sm text-gray-900", children: [
|
|
23085
|
+
breakItem.remarks || "Break",
|
|
23086
|
+
(activeBreaks.length > 1 || lineNames[breakItem.lineId]) && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-500 ml-1", children: [
|
|
23087
|
+
"\u2022 ",
|
|
23088
|
+
lineNames[breakItem.lineId] || `Line ${breakItem.lineId.substring(0, 8)}`
|
|
23089
|
+
] })
|
|
23090
|
+
] }),
|
|
23091
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Coffee, { className: "w-4 h-4 text-amber-500" })
|
|
23092
|
+
] }),
|
|
23093
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-gray-600 leading-relaxed mb-2", children: [
|
|
23094
|
+
"Currently active from ",
|
|
23095
|
+
breakItem.startTime,
|
|
23096
|
+
" to ",
|
|
23097
|
+
breakItem.endTime,
|
|
23098
|
+
". ",
|
|
23099
|
+
formatTime3(breakItem.elapsedMinutes),
|
|
23100
|
+
" elapsed of ",
|
|
23101
|
+
formatTime3(breakItem.duration),
|
|
23102
|
+
" total."
|
|
23103
|
+
] }),
|
|
23104
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23105
|
+
"div",
|
|
23106
|
+
{
|
|
23107
|
+
className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
|
|
23108
|
+
style: {
|
|
23109
|
+
width: `${Math.min(100, Math.max(0, breakItem.elapsedMinutes / breakItem.duration * 100))}%`
|
|
23110
|
+
}
|
|
23111
|
+
}
|
|
23112
|
+
) }) })
|
|
23113
|
+
] })
|
|
23114
|
+
] }),
|
|
23115
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
23116
|
+
"button",
|
|
23117
|
+
{
|
|
23118
|
+
onClick: handleDismiss,
|
|
23119
|
+
onTouchStart: () => {
|
|
23120
|
+
},
|
|
23121
|
+
className: "ml-2 text-gray-400 hover:text-gray-600 transition-colors p-2 sm:p-1 rounded-full hover:bg-gray-100 active:bg-gray-200 touch-manipulation min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0 flex items-center justify-center flex-shrink-0",
|
|
23122
|
+
"aria-label": "Dismiss notification",
|
|
23123
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
|
|
23124
|
+
}
|
|
23125
|
+
)
|
|
23126
|
+
] }) }) })
|
|
23127
|
+
},
|
|
23128
|
+
`${breakItem.lineId}-${breakItem.startTime}-${index}`
|
|
23129
|
+
)) });
|
|
23130
|
+
};
|
|
23131
|
+
var AxelNotificationPopup = ({
|
|
23132
|
+
suggestion,
|
|
23133
|
+
isVisible = true,
|
|
23134
|
+
onDismiss,
|
|
23135
|
+
className = "",
|
|
23136
|
+
axelImagePath = "/axel-profile.png"
|
|
23137
|
+
}) => {
|
|
23138
|
+
const [isDismissed, setIsDismissed] = React21.useState(false);
|
|
23139
|
+
const handleDismiss = () => {
|
|
23140
|
+
setIsDismissed(true);
|
|
23141
|
+
onDismiss?.();
|
|
23142
|
+
};
|
|
23143
|
+
if (!isVisible || isDismissed || !suggestion) {
|
|
23144
|
+
return null;
|
|
23145
|
+
}
|
|
23146
|
+
const getTypeIcon = () => {
|
|
23147
|
+
switch (suggestion.type) {
|
|
23148
|
+
case "improvement":
|
|
23149
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TrendingUp, { className: "w-4 h-4 text-blue-500" });
|
|
23150
|
+
case "alert":
|
|
23151
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-4 h-4 text-amber-500" });
|
|
23152
|
+
case "insight":
|
|
23153
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4 text-purple-500" });
|
|
23154
|
+
default:
|
|
23155
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-4 h-4 text-blue-500" });
|
|
23156
|
+
}
|
|
23157
|
+
};
|
|
23019
23158
|
return /* @__PURE__ */ jsxRuntime.jsx(AnimatePresence, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23020
23159
|
motion.div,
|
|
23021
23160
|
{
|
|
@@ -23023,58 +23162,54 @@ var BreakNotificationPopup = ({
|
|
|
23023
23162
|
animate: { opacity: 1, x: 0, y: 0 },
|
|
23024
23163
|
exit: { opacity: 0, x: 100, y: -20 },
|
|
23025
23164
|
transition: { duration: 0.3, ease: "easeOut" },
|
|
23026
|
-
className: `fixed top-24 right-4 z-
|
|
23027
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-lg overflow-hidden", children:
|
|
23165
|
+
className: `fixed top-24 right-4 z-40 max-w-xs w-full ${className}`,
|
|
23166
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white text-gray-900 rounded-lg border border-gray-200 shadow-lg overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23028
23167
|
motion.div,
|
|
23029
23168
|
{
|
|
23030
|
-
initial: { opacity: 0, y:
|
|
23169
|
+
initial: { opacity: 0, y: 10 },
|
|
23031
23170
|
animate: { opacity: 1, y: 0 },
|
|
23032
|
-
transition: { delay:
|
|
23033
|
-
className:
|
|
23171
|
+
transition: { delay: 0.1 },
|
|
23172
|
+
className: "p-3",
|
|
23034
23173
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
|
|
23035
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-
|
|
23036
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
23174
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-3 flex-1", children: [
|
|
23175
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23176
|
+
"img",
|
|
23177
|
+
{
|
|
23178
|
+
src: axelImagePath,
|
|
23179
|
+
alt: "Axel AI",
|
|
23180
|
+
className: "w-10 h-10 rounded-full object-cover border-2 border-gray-200 shadow-sm",
|
|
23181
|
+
onError: (e) => {
|
|
23182
|
+
const target = e.currentTarget;
|
|
23183
|
+
target.style.display = "none";
|
|
23184
|
+
const fallback = document.createElement("div");
|
|
23185
|
+
fallback.className = "w-10 h-10 rounded-full bg-blue-500 flex items-center justify-center text-white font-bold text-base shadow-sm border-2 border-gray-200";
|
|
23186
|
+
fallback.textContent = "A";
|
|
23187
|
+
target.parentElement?.appendChild(fallback);
|
|
23188
|
+
}
|
|
23189
|
+
}
|
|
23190
|
+
) }),
|
|
23037
23191
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
23038
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1", children: [
|
|
23039
|
-
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-
|
|
23040
|
-
|
|
23192
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [
|
|
23193
|
+
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-semibold text-sm text-gray-900", children: suggestion.title }),
|
|
23194
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: getTypeIcon() })
|
|
23041
23195
|
] }),
|
|
23042
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
23043
|
-
breakItem.startTime,
|
|
23044
|
-
" - ",
|
|
23045
|
-
breakItem.endTime
|
|
23046
|
-
] }) }),
|
|
23047
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm sm:text-xs text-gray-500", children: [
|
|
23048
|
-
formatTime3(breakItem.elapsedMinutes),
|
|
23049
|
-
" / ",
|
|
23050
|
-
formatTime3(breakItem.duration)
|
|
23051
|
-
] }) }),
|
|
23052
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full bg-gray-200 rounded-full h-1.5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
23053
|
-
"div",
|
|
23054
|
-
{
|
|
23055
|
-
className: "h-1.5 bg-blue-500 rounded-full transition-all duration-1000",
|
|
23056
|
-
style: {
|
|
23057
|
-
width: `${Math.min(100, Math.max(0, breakItem.elapsedMinutes / breakItem.duration * 100))}%`
|
|
23058
|
-
}
|
|
23059
|
-
}
|
|
23060
|
-
) }) })
|
|
23196
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-600 leading-relaxed", children: suggestion.message })
|
|
23061
23197
|
] })
|
|
23062
23198
|
] }),
|
|
23063
|
-
|
|
23199
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
23064
23200
|
"button",
|
|
23065
23201
|
{
|
|
23066
23202
|
onClick: handleDismiss,
|
|
23067
23203
|
onTouchStart: () => {
|
|
23068
23204
|
},
|
|
23069
|
-
className: "ml-2 text-gray-400 hover:text-gray-600 transition-colors p-2 sm:p-1 rounded-full hover:bg-gray-100 active:bg-gray-200 touch-manipulation min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0 flex items-center justify-center",
|
|
23205
|
+
className: "ml-2 text-gray-400 hover:text-gray-600 transition-colors p-2 sm:p-1 rounded-full hover:bg-gray-100 active:bg-gray-200 touch-manipulation min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0 flex items-center justify-center flex-shrink-0",
|
|
23070
23206
|
"aria-label": "Dismiss notification",
|
|
23071
23207
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-4 h-4 sm:w-3 sm:h-3" })
|
|
23072
23208
|
}
|
|
23073
23209
|
)
|
|
23074
23210
|
] })
|
|
23075
|
-
}
|
|
23076
|
-
|
|
23077
|
-
)) })
|
|
23211
|
+
}
|
|
23212
|
+
) })
|
|
23078
23213
|
}
|
|
23079
23214
|
) });
|
|
23080
23215
|
};
|
|
@@ -25905,12 +26040,6 @@ var WorkspaceHistoryCalendar = ({
|
|
|
25905
26040
|
] })
|
|
25906
26041
|
] });
|
|
25907
26042
|
};
|
|
25908
|
-
|
|
25909
|
-
// src/lib/constants/design-tokens.ts
|
|
25910
|
-
var designTokens = {
|
|
25911
|
-
// Typography scale with clear hierarchy
|
|
25912
|
-
typography: {
|
|
25913
|
-
body: "text-sm text-gray-600"}};
|
|
25914
26043
|
var WEEKDAYS3 = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
25915
26044
|
var getOrdinal = (n) => {
|
|
25916
26045
|
const suffix = ["th", "st", "nd", "rd"];
|
|
@@ -25959,8 +26088,19 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25959
26088
|
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
25960
26089
|
const dailyData = [];
|
|
25961
26090
|
let maxOutput = 0;
|
|
25962
|
-
let
|
|
25963
|
-
let
|
|
26091
|
+
let lastSetTarget = 0;
|
|
26092
|
+
for (let day = daysInMonth; day >= 1; day--) {
|
|
26093
|
+
const dayData = data.find((d) => {
|
|
26094
|
+
const date = new Date(d.date);
|
|
26095
|
+
return date.getDate() === day;
|
|
26096
|
+
});
|
|
26097
|
+
const shiftData = dayData ? selectedShift === "day" ? dayData.dayShift : dayData.nightShift : null;
|
|
26098
|
+
const idealOutput = shiftData ? shiftData.idealOutput : 0;
|
|
26099
|
+
if (idealOutput > 0) {
|
|
26100
|
+
lastSetTarget = idealOutput;
|
|
26101
|
+
break;
|
|
26102
|
+
}
|
|
26103
|
+
}
|
|
25964
26104
|
for (let day = 1; day <= daysInMonth; day++) {
|
|
25965
26105
|
const dayData = data.find((d) => {
|
|
25966
26106
|
const date = new Date(d.date);
|
|
@@ -25970,11 +26110,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25970
26110
|
const output = shiftData && hasRealData(shiftData) ? shiftData.output : 0;
|
|
25971
26111
|
const idealOutput = shiftData ? shiftData.idealOutput : 0;
|
|
25972
26112
|
if (output > maxOutput) maxOutput = output;
|
|
25973
|
-
|
|
25974
|
-
totalIdealOutput += idealOutput;
|
|
25975
|
-
validDaysCount++;
|
|
25976
|
-
}
|
|
25977
|
-
const color2 = output >= idealOutput ? "#00AB45" : "#E34329";
|
|
26113
|
+
const color2 = output >= lastSetTarget ? "#00AB45" : "#E34329";
|
|
25978
26114
|
dailyData.push({
|
|
25979
26115
|
hour: getOrdinal(day),
|
|
25980
26116
|
// Using ordinal format (1st, 2nd, 3rd, etc.)
|
|
@@ -25989,14 +26125,13 @@ var WorkspaceMonthlyHistory = ({
|
|
|
25989
26125
|
// Not used but keeps structure consistent
|
|
25990
26126
|
});
|
|
25991
26127
|
}
|
|
25992
|
-
const
|
|
25993
|
-
const calculatedMax = Math.max(maxOutput, avgIdealOutput);
|
|
26128
|
+
const calculatedMax = Math.max(maxOutput, lastSetTarget);
|
|
25994
26129
|
const yAxisMax = calculatedMax > 0 ? calculatedMax * 1.1 : 100;
|
|
25995
|
-
return { data: dailyData, maxOutput,
|
|
26130
|
+
return { data: dailyData, maxOutput, lastSetTarget, yAxisMax };
|
|
25996
26131
|
}, [data, month, year, selectedShift]);
|
|
25997
26132
|
const yAxisTicks = React21.useMemo(() => {
|
|
25998
26133
|
const max = chartData.yAxisMax;
|
|
25999
|
-
const target = chartData.
|
|
26134
|
+
const target = chartData.lastSetTarget;
|
|
26000
26135
|
if (!max || max <= 0) return void 0;
|
|
26001
26136
|
const desiredIntervals = 4;
|
|
26002
26137
|
const roughStep = max / desiredIntervals;
|
|
@@ -26014,7 +26149,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26014
26149
|
ticks.push(Math.round(target));
|
|
26015
26150
|
}
|
|
26016
26151
|
return Array.from(new Set(ticks)).filter((v) => v >= 0 && v <= max).sort((a, b) => a - b);
|
|
26017
|
-
}, [chartData.yAxisMax, chartData.
|
|
26152
|
+
}, [chartData.yAxisMax, chartData.lastSetTarget]);
|
|
26018
26153
|
const pieChartData = React21.useMemo(() => {
|
|
26019
26154
|
const validShifts = data.map((d) => selectedShift === "day" ? d.dayShift : d.nightShift).filter(hasRealData);
|
|
26020
26155
|
if (validShifts.length === 0) return [];
|
|
@@ -26088,10 +26223,13 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26088
26223
|
}
|
|
26089
26224
|
}, [workspaceId, onShiftChange]);
|
|
26090
26225
|
if (monthlyDataLoading) {
|
|
26091
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.
|
|
26092
|
-
|
|
26093
|
-
|
|
26094
|
-
|
|
26226
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100vh-10rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
26227
|
+
OptifyeLogoLoader_default,
|
|
26228
|
+
{
|
|
26229
|
+
size: "lg",
|
|
26230
|
+
message: "Loading monthly performance data..."
|
|
26231
|
+
}
|
|
26232
|
+
) });
|
|
26095
26233
|
}
|
|
26096
26234
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col gap-2 min-h-0 overflow-y-auto pb-6 ${className}`, children: [
|
|
26097
26235
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 border border-gray-200 rounded-lg p-1 bg-gray-50", children: [
|
|
@@ -26326,7 +26464,7 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26326
26464
|
tick: (props) => {
|
|
26327
26465
|
const { x, y, payload } = props;
|
|
26328
26466
|
const value = Math.round(payload.value);
|
|
26329
|
-
const targetValue = Math.round(chartData.
|
|
26467
|
+
const targetValue = Math.round(chartData.lastSetTarget);
|
|
26330
26468
|
const isTarget = Math.abs(value - targetValue) < 1 && targetValue > 0;
|
|
26331
26469
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
26332
26470
|
"text",
|
|
@@ -26350,10 +26488,10 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26350
26488
|
content: CustomTooltip
|
|
26351
26489
|
}
|
|
26352
26490
|
),
|
|
26353
|
-
chartData.
|
|
26491
|
+
chartData.lastSetTarget > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26354
26492
|
recharts.ReferenceLine,
|
|
26355
26493
|
{
|
|
26356
|
-
y: chartData.
|
|
26494
|
+
y: chartData.lastSetTarget,
|
|
26357
26495
|
stroke: "#E34329",
|
|
26358
26496
|
strokeDasharray: "5 5",
|
|
26359
26497
|
strokeWidth: 2
|
|
@@ -26411,11 +26549,11 @@ var WorkspaceMonthlyHistory = ({
|
|
|
26411
26549
|
]
|
|
26412
26550
|
}
|
|
26413
26551
|
) }) }),
|
|
26414
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center items-center gap-6 mt-3", children: chartData.
|
|
26552
|
+
/* @__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: [
|
|
26415
26553
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-0.5 border-t-2 border-dashed", style: { borderColor: "#E34329" } }),
|
|
26416
26554
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-gray-600", children: [
|
|
26417
26555
|
"Target: ",
|
|
26418
|
-
Math.round(chartData.
|
|
26556
|
+
Math.round(chartData.lastSetTarget),
|
|
26419
26557
|
" units/day"
|
|
26420
26558
|
] })
|
|
26421
26559
|
] }) })
|
|
@@ -26528,7 +26666,7 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
26528
26666
|
doc.roundedRect(22, y - 7, 165, 12, 2, 2, "S");
|
|
26529
26667
|
};
|
|
26530
26668
|
doc.setFillColor(245, 245, 245);
|
|
26531
|
-
doc.roundedRect(15, 95, 180,
|
|
26669
|
+
doc.roundedRect(15, 95, 180, 60, 3, 3, "F");
|
|
26532
26670
|
doc.setFontSize(18);
|
|
26533
26671
|
doc.setFont("helvetica", "bold");
|
|
26534
26672
|
doc.setTextColor(40, 40, 40);
|
|
@@ -26557,34 +26695,29 @@ var WorkspacePdfGenerator = ({ workspace, className }) => {
|
|
|
26557
26695
|
doc.text("PPH (Pieces Per Hour):", 25, kpiStartY + kpiSpacing * 3);
|
|
26558
26696
|
doc.setFont("helvetica", "bold");
|
|
26559
26697
|
doc.text(`${workspace.avg_pph.toFixed(1)} (Standard: ${workspace.pph_threshold.toFixed(1)})`, 120, kpiStartY + kpiSpacing * 3);
|
|
26560
|
-
createKPIBox(kpiStartY + kpiSpacing * 4);
|
|
26561
|
-
doc.setFont("helvetica", "normal");
|
|
26562
|
-
doc.text("Rank:", 25, kpiStartY + kpiSpacing * 4);
|
|
26563
|
-
doc.setFont("helvetica", "bold");
|
|
26564
|
-
doc.text(`${workspace.workspace_rank} of ${workspace.total_workspaces}`, 120, kpiStartY + kpiSpacing * 4);
|
|
26565
26698
|
doc.setDrawColor(180, 180, 180);
|
|
26566
26699
|
doc.setLineWidth(0.8);
|
|
26567
|
-
doc.line(20,
|
|
26700
|
+
doc.line(20, 170, 190, 170);
|
|
26568
26701
|
doc.setFillColor(245, 245, 245);
|
|
26569
|
-
doc.roundedRect(15,
|
|
26702
|
+
doc.roundedRect(15, 175, 180, 85, 3, 3, "F");
|
|
26570
26703
|
doc.setFontSize(18);
|
|
26571
26704
|
doc.setFont("helvetica", "bold");
|
|
26572
26705
|
doc.setTextColor(40, 40, 40);
|
|
26573
|
-
doc.text("Hourly Performance", 20,
|
|
26706
|
+
doc.text("Hourly Performance", 20, 185);
|
|
26574
26707
|
doc.setTextColor(0, 0, 0);
|
|
26575
26708
|
doc.setFontSize(11);
|
|
26576
26709
|
doc.setFont("helvetica", "bold");
|
|
26577
26710
|
doc.setFillColor(245, 245, 245);
|
|
26578
|
-
doc.roundedRect(20,
|
|
26579
|
-
doc.text("Time Range", 25,
|
|
26580
|
-
doc.text("Output", 95,
|
|
26581
|
-
doc.text("Target", 135,
|
|
26582
|
-
doc.text("Status", 170,
|
|
26711
|
+
doc.roundedRect(20, 177, 170, 8, 1, 1, "F");
|
|
26712
|
+
doc.text("Time Range", 25, 182);
|
|
26713
|
+
doc.text("Output", 95, 182);
|
|
26714
|
+
doc.text("Target", 135, 182);
|
|
26715
|
+
doc.text("Status", 170, 182);
|
|
26583
26716
|
doc.setLineWidth(0.2);
|
|
26584
26717
|
doc.setDrawColor(220, 220, 220);
|
|
26585
|
-
doc.line(20,
|
|
26718
|
+
doc.line(20, 185, 190, 185);
|
|
26586
26719
|
doc.setFont("helvetica", "normal");
|
|
26587
|
-
let yPos =
|
|
26720
|
+
let yPos = 191;
|
|
26588
26721
|
const hourlyData = workspace.hourly_action_counts || [];
|
|
26589
26722
|
const hourlyTarget = workspace.pph_threshold;
|
|
26590
26723
|
hourlyData.forEach((output, index) => {
|
|
@@ -27261,6 +27394,36 @@ var TimePickerDropdown = ({
|
|
|
27261
27394
|
] })
|
|
27262
27395
|
] });
|
|
27263
27396
|
};
|
|
27397
|
+
var ERROR_MAPPING = {
|
|
27398
|
+
1: {
|
|
27399
|
+
// MEDIA_ERR_ABORTED
|
|
27400
|
+
code: 1,
|
|
27401
|
+
type: "recoverable" /* RECOVERABLE */,
|
|
27402
|
+
message: "Video loading was interrupted",
|
|
27403
|
+
canRetry: true
|
|
27404
|
+
},
|
|
27405
|
+
2: {
|
|
27406
|
+
// MEDIA_ERR_NETWORK
|
|
27407
|
+
code: 2,
|
|
27408
|
+
type: "recoverable" /* RECOVERABLE */,
|
|
27409
|
+
message: "Network error - please check your internet connection",
|
|
27410
|
+
canRetry: true
|
|
27411
|
+
},
|
|
27412
|
+
3: {
|
|
27413
|
+
// MEDIA_ERR_DECODE
|
|
27414
|
+
code: 3,
|
|
27415
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27416
|
+
message: "Stream corrupted due to internet connection",
|
|
27417
|
+
canRetry: false
|
|
27418
|
+
},
|
|
27419
|
+
4: {
|
|
27420
|
+
// MEDIA_ERR_SRC_NOT_SUPPORTED
|
|
27421
|
+
code: 4,
|
|
27422
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27423
|
+
message: "Video format not supported by your browser. Please use Google Chrome.",
|
|
27424
|
+
canRetry: false
|
|
27425
|
+
}
|
|
27426
|
+
};
|
|
27264
27427
|
var videoPlayerStyles = `
|
|
27265
27428
|
.video-player-container {
|
|
27266
27429
|
width: 100%;
|
|
@@ -27491,8 +27654,21 @@ var VideoPlayer = React21__namespace.default.forwardRef(({
|
|
|
27491
27654
|
player.on("seeked", () => onSeeked?.(player));
|
|
27492
27655
|
player.on("error", () => {
|
|
27493
27656
|
const error = player.error();
|
|
27494
|
-
|
|
27495
|
-
|
|
27657
|
+
const errorCode = error?.code ?? 0;
|
|
27658
|
+
const errorInfo = ERROR_MAPPING[errorCode] || {
|
|
27659
|
+
code: errorCode || 0,
|
|
27660
|
+
type: "non_recoverable" /* NON_RECOVERABLE */,
|
|
27661
|
+
message: "Unknown playback error occurred",
|
|
27662
|
+
canRetry: false
|
|
27663
|
+
};
|
|
27664
|
+
console.error("[VideoPlayer] Video.js error:", {
|
|
27665
|
+
code: errorCode,
|
|
27666
|
+
type: errorInfo.type,
|
|
27667
|
+
message: errorInfo.message,
|
|
27668
|
+
canRetry: errorInfo.canRetry,
|
|
27669
|
+
originalError: error
|
|
27670
|
+
});
|
|
27671
|
+
onError?.(player, errorInfo);
|
|
27496
27672
|
});
|
|
27497
27673
|
if (src) {
|
|
27498
27674
|
const isHLS = src.endsWith(".m3u8") || src.startsWith("#EXTM3U");
|
|
@@ -28335,7 +28511,10 @@ var FilterDialogTrigger = ({
|
|
|
28335
28511
|
}
|
|
28336
28512
|
);
|
|
28337
28513
|
};
|
|
28338
|
-
var getSeverityIcon = (severity) => {
|
|
28514
|
+
var getSeverityIcon = (severity, categoryId) => {
|
|
28515
|
+
if (categoryId === "idle_time" || categoryId === "low_value" || categoryId === "longest-idles") {
|
|
28516
|
+
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-red-500" });
|
|
28517
|
+
}
|
|
28339
28518
|
switch (severity) {
|
|
28340
28519
|
case "high":
|
|
28341
28520
|
return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3 w-3 text-red-500" });
|
|
@@ -28562,17 +28741,17 @@ var FileManagerFilters = ({
|
|
|
28562
28741
|
const colorClasses = getColorClasses(category.color);
|
|
28563
28742
|
const clipNodes = filteredClips.map((clip, index) => {
|
|
28564
28743
|
const timeString = new Date(clip.clip_timestamp).toLocaleTimeString("en-US", {
|
|
28565
|
-
hour12:
|
|
28566
|
-
hour: "
|
|
28744
|
+
hour12: true,
|
|
28745
|
+
hour: "numeric",
|
|
28567
28746
|
minute: "2-digit",
|
|
28568
28747
|
timeZone: timezone
|
|
28569
28748
|
// Use database timezone for display
|
|
28570
28749
|
});
|
|
28571
28750
|
return {
|
|
28572
28751
|
id: clip.id,
|
|
28573
|
-
label: `${timeString}
|
|
28752
|
+
label: `${timeString}${clip.duration && category.id !== "idle_time" ? ` - (${clip.duration.toFixed(1)}s)` : ""}`,
|
|
28574
28753
|
type: "video",
|
|
28575
|
-
icon: getSeverityIcon(clip.severity),
|
|
28754
|
+
icon: getSeverityIcon(clip.severity, category.id),
|
|
28576
28755
|
timestamp: clip.clip_timestamp,
|
|
28577
28756
|
severity: clip.severity,
|
|
28578
28757
|
clipId: clip.clipId,
|
|
@@ -28612,9 +28791,9 @@ var FileManagerFilters = ({
|
|
|
28612
28791
|
children: (percentileClips["fast-cycles"] || []).map((clip, index) => ({
|
|
28613
28792
|
id: clip.id,
|
|
28614
28793
|
// Remove prefix to match currentVideoId
|
|
28615
|
-
label: `${clip.timestamp}
|
|
28794
|
+
label: `${clip.timestamp}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
|
|
28616
28795
|
type: "video",
|
|
28617
|
-
icon: getSeverityIcon(clip.severity),
|
|
28796
|
+
icon: getSeverityIcon(clip.severity, "fast-cycles"),
|
|
28618
28797
|
timestamp: clip.creation_timestamp,
|
|
28619
28798
|
severity: clip.severity,
|
|
28620
28799
|
clipId: clip.id,
|
|
@@ -28635,9 +28814,9 @@ var FileManagerFilters = ({
|
|
|
28635
28814
|
children: (percentileClips["slow-cycles"] || []).map((clip, index) => ({
|
|
28636
28815
|
id: clip.id,
|
|
28637
28816
|
// Remove prefix to match currentVideoId
|
|
28638
|
-
label: `${clip.timestamp}
|
|
28817
|
+
label: `${clip.timestamp}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ""}`,
|
|
28639
28818
|
type: "video",
|
|
28640
|
-
icon: getSeverityIcon(clip.severity),
|
|
28819
|
+
icon: getSeverityIcon(clip.severity, "slow-cycles"),
|
|
28641
28820
|
timestamp: clip.creation_timestamp,
|
|
28642
28821
|
severity: clip.severity,
|
|
28643
28822
|
clipId: clip.id,
|
|
@@ -28658,9 +28837,9 @@ var FileManagerFilters = ({
|
|
|
28658
28837
|
isPercentile: true,
|
|
28659
28838
|
children: (percentileClips['idle-times'] || []).map((clip, index) => ({
|
|
28660
28839
|
id: clip.id, // Remove prefix to match currentVideoId
|
|
28661
|
-
label: `${clip.timestamp}
|
|
28840
|
+
label: `${clip.timestamp}${clip.cycle_time_seconds ? ` - (${clip.cycle_time_seconds.toFixed(1)}s)` : ''}`,
|
|
28662
28841
|
type: 'video' as const,
|
|
28663
|
-
icon: getSeverityIcon(clip.severity),
|
|
28842
|
+
icon: getSeverityIcon(clip.severity, 'longest-idles'),
|
|
28664
28843
|
timestamp: clip.creation_timestamp,
|
|
28665
28844
|
severity: clip.severity,
|
|
28666
28845
|
clipId: clip.id,
|
|
@@ -28670,7 +28849,7 @@ var FileManagerFilters = ({
|
|
|
28670
28849
|
];
|
|
28671
28850
|
percentileCategories.forEach((category) => {
|
|
28672
28851
|
if (category.count > 0 && shouldShowCategory(category.id)) {
|
|
28673
|
-
tree.
|
|
28852
|
+
tree.push(category);
|
|
28674
28853
|
}
|
|
28675
28854
|
});
|
|
28676
28855
|
return tree;
|
|
@@ -28715,7 +28894,7 @@ var FileManagerFilters = ({
|
|
|
28715
28894
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
28716
28895
|
"div",
|
|
28717
28896
|
{
|
|
28718
|
-
className: `flex items-center cursor-pointer transition-all duration-300 ease-out group relative overflow-hidden ${node.type === "category" && depth === 0 ? `py-3 px-4 rounded-2xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-blue-50/30 hover:shadow-lg hover:shadow-blue-100/20 hover:scale-[1.02] hover:-translate-y-0.5 ${isActive ? "bg-gradient-to-r from-blue-50 via-blue-50/80 to-indigo-50/60 border border-blue-200/50 shadow-lg shadow-blue-100/30 scale-[1.02]" : "border border-transparent"}` : `py-2 px-3 rounded-xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-slate-50/50 hover:shadow-sm ${isActive ? "bg-gradient-to-r from-blue-50/80 to-blue-50/40 border border-blue-200/30 shadow-sm" : "border border-transparent"} ${isCurrentVideo ? "bg-gradient-to-r from-
|
|
28897
|
+
className: `flex items-center cursor-pointer transition-all duration-300 ease-out group relative overflow-hidden ${node.type === "category" && depth === 0 ? `py-3 px-4 rounded-2xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-blue-50/30 hover:shadow-lg hover:shadow-blue-100/20 hover:scale-[1.02] hover:-translate-y-0.5 ${isActive ? "bg-gradient-to-r from-blue-50 via-blue-50/80 to-indigo-50/60 border border-blue-200/50 shadow-lg shadow-blue-100/30 scale-[1.02]" : "border border-transparent"}` : `py-2 px-3 rounded-xl hover:bg-gradient-to-r hover:from-slate-50 hover:to-slate-50/50 hover:shadow-sm ${isActive ? "bg-gradient-to-r from-blue-50/80 to-blue-50/40 border border-blue-200/30 shadow-sm" : "border border-transparent"} ${isCurrentVideo ? "bg-gradient-to-r from-blue-50 to-blue-50/60 border border-blue-200/50 shadow-md shadow-blue-100/20" : ""}`} ${node.type === "video" ? "ml-6" : ""}`,
|
|
28719
28898
|
onClick: () => handleNodeClick(node),
|
|
28720
28899
|
children: [
|
|
28721
28900
|
hasChildren && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -28732,13 +28911,10 @@ var FileManagerFilters = ({
|
|
|
28732
28911
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex-shrink-0 mr-3 ${node.type === "category" || node.type === "percentile-category" ? "p-2 rounded-lg shadow-sm group-hover:scale-110 transition-transform duration-200" : "p-0.5"} ${colorClasses && (node.type === "category" || node.type === "percentile-category") ? `${colorClasses.bg} border border-white/60` : ""}`, children: node.icon }),
|
|
28733
28912
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex items-center justify-between", children: [
|
|
28734
28913
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
|
|
28735
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `font-semibold tracking-tight ${node.type === "category" || node.type === "percentile-category" ? "text-slate-800 text-sm" : "text-slate-700 text-xs"} ${isCurrentVideo ? "text-
|
|
28914
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `font-semibold tracking-tight ${node.type === "category" || node.type === "percentile-category" ? "text-slate-800 text-sm" : "text-slate-700 text-xs"} ${isCurrentVideo ? "text-blue-700 font-bold" : ""} group-hover:text-slate-900 transition-colors duration-200`, children: node.label }),
|
|
28736
28915
|
node.type === "category" && categories.find((c) => c.id === node.id)?.description && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: categories.find((c) => c.id === node.id)?.description }),
|
|
28737
28916
|
node.type === "percentile-category" && node.subtitle && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 mt-0.5 font-normal", children: node.subtitle }),
|
|
28738
|
-
node.type === "video" && node.severity && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: /* @__PURE__ */ jsxRuntime.
|
|
28739
|
-
node.severity,
|
|
28740
|
-
" priority"
|
|
28741
|
-
] }) })
|
|
28917
|
+
node.type === "video" && node.severity && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-slate-500 capitalize mt-0.5 font-medium", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-flex items-center px-1.5 py-0.5 rounded-md text-xs font-medium ${node.categoryId === "idle_time" || node.categoryId === "low_value" ? "bg-red-100 text-red-700" : node.severity === "high" ? "bg-red-100 text-red-700" : node.severity === "medium" ? "bg-yellow-100 text-yellow-700" : "bg-green-100 text-green-700"}`, children: node.categoryId === "idle_time" || node.categoryId === "low_value" ? "Idle" : node.severity === "low" ? "Fast" : node.severity === "medium" ? "Average" : "Slow" }) })
|
|
28742
28918
|
] }),
|
|
28743
28919
|
node.count !== void 0 && (node.type === "category" || node.type === "percentile-category") && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center ml-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2.5 py-1 text-sm font-bold rounded-lg shadow-sm border backdrop-blur-sm flex-shrink-0 group-hover:scale-105 transition-all duration-200 ${colorClasses ? `${colorClasses.bg} ${colorClasses.text} ${colorClasses.border} bg-opacity-80` : "bg-slate-100/80 text-slate-700 border-slate-200/60"}`, children: node.count }) })
|
|
28744
28920
|
] })
|
|
@@ -29387,7 +29563,12 @@ var BottlenecksContent = ({
|
|
|
29387
29563
|
} catch (err) {
|
|
29388
29564
|
console.error("[BottlenecksContent] Error fetching clip counts:", err);
|
|
29389
29565
|
if (isMountedRef.current) {
|
|
29390
|
-
setError(
|
|
29566
|
+
setError({
|
|
29567
|
+
type: "fatal",
|
|
29568
|
+
message: "Failed to load clip counts. Please try again.",
|
|
29569
|
+
canSkip: false,
|
|
29570
|
+
canRetry: true
|
|
29571
|
+
});
|
|
29391
29572
|
setIsLoading(false);
|
|
29392
29573
|
}
|
|
29393
29574
|
} finally {
|
|
@@ -29471,7 +29652,12 @@ var BottlenecksContent = ({
|
|
|
29471
29652
|
} catch (err) {
|
|
29472
29653
|
console.error("Error loading first video for category:", err);
|
|
29473
29654
|
if (isMountedRef.current) {
|
|
29474
|
-
setError(
|
|
29655
|
+
setError({
|
|
29656
|
+
type: "fatal",
|
|
29657
|
+
message: "Failed to load clips. Please try again.",
|
|
29658
|
+
canSkip: false,
|
|
29659
|
+
canRetry: true
|
|
29660
|
+
});
|
|
29475
29661
|
setIsCategoryLoading(false);
|
|
29476
29662
|
}
|
|
29477
29663
|
} finally {
|
|
@@ -29753,7 +29939,12 @@ var BottlenecksContent = ({
|
|
|
29753
29939
|
} catch (error2) {
|
|
29754
29940
|
console.error(`[BottlenecksContent] Error loading clip by ID (${clipId}):`, error2);
|
|
29755
29941
|
if (isMountedRef.current) {
|
|
29756
|
-
setError(
|
|
29942
|
+
setError({
|
|
29943
|
+
type: "fatal",
|
|
29944
|
+
message: "Failed to load selected clip. Please try again.",
|
|
29945
|
+
canSkip: true,
|
|
29946
|
+
canRetry: true
|
|
29947
|
+
});
|
|
29757
29948
|
clearLoadingState();
|
|
29758
29949
|
}
|
|
29759
29950
|
}
|
|
@@ -29776,7 +29967,12 @@ var BottlenecksContent = ({
|
|
|
29776
29967
|
}
|
|
29777
29968
|
} catch (error2) {
|
|
29778
29969
|
console.error(`[BottlenecksContent] Error in legacy loadAndPlayClip:`, error2);
|
|
29779
|
-
setError(
|
|
29970
|
+
setError({
|
|
29971
|
+
type: "fatal",
|
|
29972
|
+
message: "Failed to load selected clip. Please try again.",
|
|
29973
|
+
canSkip: true,
|
|
29974
|
+
canRetry: true
|
|
29975
|
+
});
|
|
29780
29976
|
setIsNavigating(false);
|
|
29781
29977
|
}
|
|
29782
29978
|
}, [workspaceId, s3ClipsService, date, effectiveShift, loadAndPlayClipById]);
|
|
@@ -29816,7 +30012,12 @@ var BottlenecksContent = ({
|
|
|
29816
30012
|
}
|
|
29817
30013
|
} catch (error2) {
|
|
29818
30014
|
console.error(`[handleNext] Error navigating:`, error2);
|
|
29819
|
-
setError(
|
|
30015
|
+
setError({
|
|
30016
|
+
type: "fatal",
|
|
30017
|
+
message: "Failed to navigate to next clip",
|
|
30018
|
+
canSkip: true,
|
|
30019
|
+
canRetry: true
|
|
30020
|
+
});
|
|
29820
30021
|
clearLoadingState();
|
|
29821
30022
|
}
|
|
29822
30023
|
}, [clearLoadingState, s3ClipsService]);
|
|
@@ -29852,7 +30053,12 @@ var BottlenecksContent = ({
|
|
|
29852
30053
|
}
|
|
29853
30054
|
} catch (error2) {
|
|
29854
30055
|
console.error(`[handlePrevious] Error navigating:`, error2);
|
|
29855
|
-
setError(
|
|
30056
|
+
setError({
|
|
30057
|
+
type: "fatal",
|
|
30058
|
+
message: "Failed to navigate to previous clip",
|
|
30059
|
+
canSkip: true,
|
|
30060
|
+
canRetry: true
|
|
30061
|
+
});
|
|
29856
30062
|
clearLoadingState();
|
|
29857
30063
|
}
|
|
29858
30064
|
}, [clearLoadingState, s3ClipsService]);
|
|
@@ -29865,7 +30071,7 @@ var BottlenecksContent = ({
|
|
|
29865
30071
|
const handleVideoReady = React21.useCallback((player) => {
|
|
29866
30072
|
console.log("Video.js player ready - NOT clearing loading (wait for playing event)");
|
|
29867
30073
|
videoRetryCountRef.current = 0;
|
|
29868
|
-
if (error?.
|
|
30074
|
+
if (error?.isRetrying) {
|
|
29869
30075
|
setError(null);
|
|
29870
30076
|
}
|
|
29871
30077
|
}, [error]);
|
|
@@ -29910,20 +30116,53 @@ var BottlenecksContent = ({
|
|
|
29910
30116
|
}, [clearLoadingState]);
|
|
29911
30117
|
const handleVideoLoadingChange = React21.useCallback((isLoading2) => {
|
|
29912
30118
|
console.log(`[BottlenecksContent] Video loading state changed: ${isLoading2}`);
|
|
30119
|
+
if (error && error.type === "fatal") {
|
|
30120
|
+
console.log(`[BottlenecksContent] Ignoring loading state change - fatal error is showing`);
|
|
30121
|
+
return;
|
|
30122
|
+
}
|
|
29913
30123
|
setIsVideoBuffering(isLoading2);
|
|
29914
|
-
}, []);
|
|
30124
|
+
}, [error]);
|
|
29915
30125
|
const handleVideoEnded = React21.useCallback((player) => {
|
|
29916
30126
|
handleNext();
|
|
29917
30127
|
}, [handleNext]);
|
|
29918
30128
|
const videoRetryCountRef = React21.useRef(0);
|
|
29919
|
-
const handleVideoError = React21.useCallback((player,
|
|
29920
|
-
console.error("Video.js error:",
|
|
30129
|
+
const handleVideoError = React21.useCallback((player, errorInfo) => {
|
|
30130
|
+
console.error("[BottlenecksContent] Video.js error:", errorInfo);
|
|
29921
30131
|
setIsPlaying(false);
|
|
30132
|
+
setIsVideoBuffering(false);
|
|
30133
|
+
const errorCode = errorInfo?.code || 0;
|
|
30134
|
+
const canRetry = errorInfo?.canRetry ?? false;
|
|
30135
|
+
const errorMessage = errorInfo?.message || "Unknown error";
|
|
30136
|
+
console.log(`[Video Error] Code: ${errorCode}, Can Retry: ${canRetry}, Message: ${errorMessage}`);
|
|
30137
|
+
if (!canRetry) {
|
|
30138
|
+
console.log("[Video Error] Non-recoverable error - showing error overlay immediately");
|
|
30139
|
+
setError({
|
|
30140
|
+
type: "fatal",
|
|
30141
|
+
code: errorCode,
|
|
30142
|
+
message: errorMessage,
|
|
30143
|
+
canSkip: true,
|
|
30144
|
+
canRetry: false
|
|
30145
|
+
});
|
|
30146
|
+
clearLoadingState();
|
|
30147
|
+
videoRetryCountRef.current = 0;
|
|
30148
|
+
trackCoreEvent("clips_video_error_non_recoverable", {
|
|
30149
|
+
workspaceId,
|
|
30150
|
+
category: activeFilterRef.current,
|
|
30151
|
+
videoId: currentVideo?.id,
|
|
30152
|
+
errorCode,
|
|
30153
|
+
errorMessage
|
|
30154
|
+
});
|
|
30155
|
+
return;
|
|
30156
|
+
}
|
|
29922
30157
|
if (videoRetryCountRef.current < 3 && currentVideo) {
|
|
29923
30158
|
videoRetryCountRef.current++;
|
|
29924
30159
|
const retryDelay = 1e3 * videoRetryCountRef.current;
|
|
29925
|
-
console.log(`[Video Error] Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
|
|
29926
|
-
setError(
|
|
30160
|
+
console.log(`[Video Error] Recoverable error - Retrying... Attempt ${videoRetryCountRef.current}/3 in ${retryDelay}ms`);
|
|
30161
|
+
setError({
|
|
30162
|
+
type: "retrying",
|
|
30163
|
+
message: `Retrying... (${videoRetryCountRef.current}/3)`,
|
|
30164
|
+
isRetrying: true
|
|
30165
|
+
});
|
|
29927
30166
|
setTimeout(() => {
|
|
29928
30167
|
if (videoRef.current && currentVideo && isMountedRef.current) {
|
|
29929
30168
|
setError(null);
|
|
@@ -29931,16 +30170,26 @@ var BottlenecksContent = ({
|
|
|
29931
30170
|
}
|
|
29932
30171
|
}, retryDelay);
|
|
29933
30172
|
} else {
|
|
29934
|
-
|
|
30173
|
+
console.log("[Video Error] Retries exhausted - showing final error overlay");
|
|
30174
|
+
setError({
|
|
30175
|
+
type: "fatal",
|
|
30176
|
+
code: errorCode,
|
|
30177
|
+
message: errorMessage,
|
|
30178
|
+
canSkip: true,
|
|
30179
|
+
canRetry: true
|
|
30180
|
+
// Allow manual retry for network errors
|
|
30181
|
+
});
|
|
29935
30182
|
videoRetryCountRef.current = 0;
|
|
29936
30183
|
trackCoreEvent("clips_video_error_final", {
|
|
29937
30184
|
workspaceId,
|
|
29938
30185
|
category: activeFilterRef.current,
|
|
29939
30186
|
videoId: currentVideo?.id,
|
|
29940
|
-
|
|
30187
|
+
errorCode,
|
|
30188
|
+
errorMessage,
|
|
30189
|
+
attempts: 3
|
|
29941
30190
|
});
|
|
29942
30191
|
}
|
|
29943
|
-
}, [currentVideo, workspaceId]);
|
|
30192
|
+
}, [currentVideo, workspaceId, clearLoadingState]);
|
|
29944
30193
|
React21.useEffect(() => {
|
|
29945
30194
|
isMountedRef.current = true;
|
|
29946
30195
|
return () => {
|
|
@@ -29958,9 +30207,13 @@ var BottlenecksContent = ({
|
|
|
29958
30207
|
}, [s3ClipsService]);
|
|
29959
30208
|
React21.useEffect(() => {
|
|
29960
30209
|
if (filteredVideos.length > 0 && currentIndex < filteredVideos.length) {
|
|
30210
|
+
if (error && error.type === "fatal") {
|
|
30211
|
+
console.log("[BottlenecksContent] Not clearing fatal error on video change - let user handle it");
|
|
30212
|
+
return;
|
|
30213
|
+
}
|
|
29961
30214
|
setError(null);
|
|
29962
30215
|
}
|
|
29963
|
-
}, [currentIndex, filteredVideos]);
|
|
30216
|
+
}, [currentIndex, filteredVideos, error]);
|
|
29964
30217
|
React21.useEffect(() => {
|
|
29965
30218
|
if (!isTransitioning && pendingVideo) {
|
|
29966
30219
|
const timer = setTimeout(() => {
|
|
@@ -30035,11 +30288,11 @@ var BottlenecksContent = ({
|
|
|
30035
30288
|
if ((isLoading || clipTypesLoading) && allVideos.length === 0 && Object.keys(mergedCounts).length === 0) {
|
|
30036
30289
|
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..." }) });
|
|
30037
30290
|
}
|
|
30038
|
-
if (error || clipTypesError) {
|
|
30291
|
+
if (error && error.type === "fatal" && !hasInitialLoad || clipTypesError) {
|
|
30039
30292
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
|
|
30040
30293
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
|
|
30041
30294
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold text-red-700 mb-1", children: "Error Loading Clips" }),
|
|
30042
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error || clipTypesError })
|
|
30295
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 max-w-md", children: error?.message || clipTypesError })
|
|
30043
30296
|
] });
|
|
30044
30297
|
}
|
|
30045
30298
|
const categoriesToShow = clipTypes.length > 0 ? clipTypes : [];
|
|
@@ -30086,7 +30339,7 @@ var BottlenecksContent = ({
|
|
|
30086
30339
|
),
|
|
30087
30340
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-1", children: [
|
|
30088
30341
|
/* @__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" }),
|
|
30089
|
-
error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-
|
|
30342
|
+
error && error.type === "retrying" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-orange-600 font-medium", children: error.message })
|
|
30090
30343
|
] }),
|
|
30091
30344
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30092
30345
|
"button",
|
|
@@ -30100,7 +30353,6 @@ var BottlenecksContent = ({
|
|
|
30100
30353
|
)
|
|
30101
30354
|
] })
|
|
30102
30355
|
] }) }),
|
|
30103
|
-
/* Show video if we have filtered videos and current video, or show loading */
|
|
30104
30356
|
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: [
|
|
30105
30357
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30106
30358
|
CroppedVideoPlayer,
|
|
@@ -30132,30 +30384,44 @@ var BottlenecksContent = ({
|
|
|
30132
30384
|
}
|
|
30133
30385
|
}
|
|
30134
30386
|
),
|
|
30135
|
-
(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..." }) }),
|
|
30136
|
-
!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..." }) }),
|
|
30137
|
-
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 flex items-center justify-center bg-black/
|
|
30387
|
+
(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..." }) }),
|
|
30388
|
+
!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..." }) }),
|
|
30389
|
+
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: [
|
|
30390
|
+
/* @__PURE__ */ jsxRuntime.jsx(OptifyeLogoLoader_default, { size: "md" }),
|
|
30391
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-white text-sm mt-4 font-medium", children: error.message })
|
|
30392
|
+
] }) }),
|
|
30393
|
+
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: [
|
|
30138
30394
|
/* @__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" }) }),
|
|
30139
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold mb-2", children: "
|
|
30140
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-300 mb-
|
|
30141
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
30142
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30143
|
-
|
|
30144
|
-
|
|
30145
|
-
|
|
30146
|
-
|
|
30147
|
-
|
|
30148
|
-
|
|
30149
|
-
|
|
30150
|
-
|
|
30151
|
-
|
|
30152
|
-
|
|
30153
|
-
|
|
30154
|
-
|
|
30155
|
-
|
|
30156
|
-
|
|
30157
|
-
|
|
30158
|
-
|
|
30395
|
+
/* @__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" }),
|
|
30396
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-300 mb-6", children: error.message }),
|
|
30397
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 justify-center", children: [
|
|
30398
|
+
error.canSkip && /* @__PURE__ */ jsxRuntime.jsx(
|
|
30399
|
+
"button",
|
|
30400
|
+
{
|
|
30401
|
+
onClick: () => {
|
|
30402
|
+
setError(null);
|
|
30403
|
+
videoRetryCountRef.current = 0;
|
|
30404
|
+
handleNext();
|
|
30405
|
+
},
|
|
30406
|
+
className: "px-5 py-2.5 bg-blue-600 hover:bg-blue-700 rounded-md text-sm font-medium transition-colors",
|
|
30407
|
+
children: "Skip to Next Clip"
|
|
30408
|
+
}
|
|
30409
|
+
),
|
|
30410
|
+
error.canRetry && /* @__PURE__ */ jsxRuntime.jsx(
|
|
30411
|
+
"button",
|
|
30412
|
+
{
|
|
30413
|
+
onClick: () => {
|
|
30414
|
+
setError(null);
|
|
30415
|
+
videoRetryCountRef.current = 0;
|
|
30416
|
+
if (videoRef.current) {
|
|
30417
|
+
videoRef.current.dispose();
|
|
30418
|
+
}
|
|
30419
|
+
},
|
|
30420
|
+
className: "px-5 py-2.5 bg-gray-600 hover:bg-gray-700 rounded-md text-sm font-medium transition-colors",
|
|
30421
|
+
children: "Retry"
|
|
30422
|
+
}
|
|
30423
|
+
)
|
|
30424
|
+
] })
|
|
30159
30425
|
] }) }),
|
|
30160
30426
|
(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: [
|
|
30161
30427
|
/* @__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` }),
|
|
@@ -36889,6 +37155,8 @@ function HomeView({
|
|
|
36889
37155
|
const [errorMessage, setErrorMessage] = React21.useState(null);
|
|
36890
37156
|
const [displayNamesInitialized, setDisplayNamesInitialized] = React21.useState(false);
|
|
36891
37157
|
const [hasInitialDataLoaded, setHasInitialDataLoaded] = React21.useState(false);
|
|
37158
|
+
const [axelSuggestion, setAxelSuggestion] = React21.useState(null);
|
|
37159
|
+
const [showAxelNotification, setShowAxelNotification] = React21.useState(false);
|
|
36892
37160
|
const dashboardConfig = useDashboardConfig();
|
|
36893
37161
|
const timezone = useAppTimezone();
|
|
36894
37162
|
React21.useEffect(() => {
|
|
@@ -36963,12 +37231,7 @@ function HomeView({
|
|
|
36963
37231
|
}, []);
|
|
36964
37232
|
const handleWorkspaceHoverEnd = React21.useCallback((workspaceId) => {
|
|
36965
37233
|
}, []);
|
|
36966
|
-
const memoizedWorkspaceMetrics =
|
|
36967
|
-
// Only update reference if meaningful properties change
|
|
36968
|
-
workspaceMetrics.length,
|
|
36969
|
-
// Use stable string representation instead of spreading array
|
|
36970
|
-
JSON.stringify(workspaceMetrics.map((w) => `${w.workspace_uuid}-${Math.round(w.efficiency)}-${w.trend}`))
|
|
36971
|
-
]);
|
|
37234
|
+
const memoizedWorkspaceMetrics = workspaceMetrics;
|
|
36972
37235
|
const memoizedKPIs = React21.useMemo(() => kpis, [
|
|
36973
37236
|
// Only update reference when values change by at least 1%
|
|
36974
37237
|
kpis?.efficiency?.value ? Math.round(kpis.efficiency.value) : null,
|
|
@@ -36996,6 +37259,9 @@ function HomeView({
|
|
|
36996
37259
|
setIsChangingFilter(true);
|
|
36997
37260
|
setSelectedLineId(value);
|
|
36998
37261
|
}, []);
|
|
37262
|
+
const handleDismissAxelNotification = React21.useCallback(() => {
|
|
37263
|
+
setShowAxelNotification(false);
|
|
37264
|
+
}, []);
|
|
36999
37265
|
React21.useEffect(() => {
|
|
37000
37266
|
if (!metricsLoading && !kpisLoading && isChangingFilter) {
|
|
37001
37267
|
if (workspaceMetrics.length > 0 || selectedLineId === factoryViewId) {
|
|
@@ -37107,6 +37373,14 @@ function HomeView({
|
|
|
37107
37373
|
lineNames,
|
|
37108
37374
|
isVisible: !breaksLoading && !breaksError
|
|
37109
37375
|
}
|
|
37376
|
+
),
|
|
37377
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
37378
|
+
AxelNotificationPopup,
|
|
37379
|
+
{
|
|
37380
|
+
suggestion: axelSuggestion,
|
|
37381
|
+
isVisible: showAxelNotification,
|
|
37382
|
+
onDismiss: handleDismissAxelNotification
|
|
37383
|
+
}
|
|
37110
37384
|
)
|
|
37111
37385
|
] })
|
|
37112
37386
|
}
|
|
@@ -38540,7 +38814,7 @@ var KPIsOverviewView = ({
|
|
|
38540
38814
|
var KPIsOverviewView_default = KPIsOverviewView;
|
|
38541
38815
|
var IsolatedTimer = React21.memo(() => {
|
|
38542
38816
|
return /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {});
|
|
38543
|
-
}
|
|
38817
|
+
});
|
|
38544
38818
|
IsolatedTimer.displayName = "IsolatedTimer";
|
|
38545
38819
|
var HeaderRibbon = React21.memo(({
|
|
38546
38820
|
currentDate,
|
|
@@ -38548,42 +38822,49 @@ var HeaderRibbon = React21.memo(({
|
|
|
38548
38822
|
shiftId,
|
|
38549
38823
|
getShiftIcon,
|
|
38550
38824
|
getShiftName
|
|
38551
|
-
}) =>
|
|
38552
|
-
|
|
38553
|
-
|
|
38554
|
-
|
|
38555
|
-
|
|
38556
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children:
|
|
38825
|
+
}) => {
|
|
38826
|
+
const shiftIcon = React21.useMemo(() => getShiftIcon(shiftId), [getShiftIcon, shiftId]);
|
|
38827
|
+
const shiftName = React21.useMemo(() => getShiftName(shiftId), [getShiftName, shiftId]);
|
|
38828
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
38829
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sm:hidden mt-3 flex items-center justify-center gap-2", children: [
|
|
38830
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-gray-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: currentMobileDate }) }),
|
|
38831
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "inline-flex items-center gap-1 px-2.5 py-1 bg-gray-100 rounded-full", children: [
|
|
38832
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-gray-700 scale-90", children: shiftIcon }),
|
|
38833
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-700", children: shiftName })
|
|
38834
|
+
] }),
|
|
38835
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex items-center px-2.5 py-1 bg-green-100 rounded-full", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-green-700", children: /* @__PURE__ */ jsxRuntime.jsx(IsolatedTimer, {}) }) })
|
|
38557
38836
|
] }),
|
|
38558
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
38559
|
-
|
|
38560
|
-
|
|
38561
|
-
|
|
38562
|
-
|
|
38563
|
-
|
|
38564
|
-
|
|
38565
|
-
|
|
38566
|
-
|
|
38567
|
-
|
|
38568
|
-
|
|
38569
|
-
" Shift"
|
|
38837
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block mt-3 bg-blue-50 px-3 py-2 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3 md:gap-4", children: [
|
|
38838
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-base md:text-lg font-medium text-blue-600", children: /* @__PURE__ */ jsxRuntime.jsx(IsolatedTimer, {}) }),
|
|
38839
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-300" }),
|
|
38840
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm md:text-base font-medium text-blue-600", children: currentDate }),
|
|
38841
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-px h-4 bg-blue-300" }),
|
|
38842
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
38843
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-blue-600", children: shiftIcon }),
|
|
38844
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm md:text-base font-medium text-blue-600", children: [
|
|
38845
|
+
shiftName,
|
|
38846
|
+
" Shift"
|
|
38847
|
+
] })
|
|
38570
38848
|
] })
|
|
38571
|
-
] })
|
|
38572
|
-
] })
|
|
38573
|
-
|
|
38849
|
+
] }) })
|
|
38850
|
+
] });
|
|
38851
|
+
});
|
|
38574
38852
|
HeaderRibbon.displayName = "HeaderRibbon";
|
|
38575
38853
|
var MobileWorkspaceCard = React21.memo(({
|
|
38576
38854
|
workspace,
|
|
38577
38855
|
rank,
|
|
38578
38856
|
cardClass,
|
|
38579
38857
|
onWorkspaceClick,
|
|
38580
|
-
getMedalIcon
|
|
38581
|
-
getLineName
|
|
38858
|
+
getMedalIcon
|
|
38582
38859
|
}) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
38583
38860
|
"div",
|
|
38584
38861
|
{
|
|
38585
38862
|
onClick: () => onWorkspaceClick(workspace, rank),
|
|
38586
|
-
className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] transition-all cursor-pointer`,
|
|
38863
|
+
className: `${cardClass} p-3 rounded-lg border shadow-sm active:scale-[0.98] transition-all duration-300 cursor-pointer`,
|
|
38864
|
+
style: {
|
|
38865
|
+
willChange: "opacity, transform",
|
|
38866
|
+
animation: "fadeIn 0.3s ease-in-out"
|
|
38867
|
+
},
|
|
38587
38868
|
children: [
|
|
38588
38869
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
38589
38870
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
@@ -38595,8 +38876,8 @@ var MobileWorkspaceCard = React21.memo(({
|
|
|
38595
38876
|
getMedalIcon(rank)
|
|
38596
38877
|
] }),
|
|
38597
38878
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
38598
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children:
|
|
38599
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children:
|
|
38879
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-semibold text-gray-900", children: workspace.displayName }),
|
|
38880
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-gray-500", children: workspace.lineName })
|
|
38600
38881
|
] })
|
|
38601
38882
|
] }),
|
|
38602
38883
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
|
|
@@ -38626,27 +38907,32 @@ var MobileWorkspaceCard = React21.memo(({
|
|
|
38626
38907
|
] })
|
|
38627
38908
|
]
|
|
38628
38909
|
}
|
|
38629
|
-
))
|
|
38910
|
+
), (prevProps, nextProps) => {
|
|
38911
|
+
return prevProps.rank === nextProps.rank && prevProps.cardClass === nextProps.cardClass && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName && prevProps.onWorkspaceClick === nextProps.onWorkspaceClick && prevProps.getMedalIcon === nextProps.getMedalIcon;
|
|
38912
|
+
});
|
|
38630
38913
|
MobileWorkspaceCard.displayName = "MobileWorkspaceCard";
|
|
38631
38914
|
var DesktopWorkspaceRow = React21.memo(({
|
|
38632
38915
|
workspace,
|
|
38633
38916
|
index,
|
|
38634
38917
|
rowClass,
|
|
38635
38918
|
onWorkspaceClick,
|
|
38636
|
-
getMedalIcon
|
|
38637
|
-
getLineName
|
|
38919
|
+
getMedalIcon
|
|
38638
38920
|
}) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
38639
38921
|
"tr",
|
|
38640
38922
|
{
|
|
38641
38923
|
onClick: () => onWorkspaceClick(workspace, index + 1),
|
|
38642
|
-
className: `${rowClass} hover:bg-gray-50/90 transition-
|
|
38924
|
+
className: `${rowClass} hover:bg-gray-50/90 transition-all duration-300 cursor-pointer group`,
|
|
38925
|
+
style: {
|
|
38926
|
+
willChange: "opacity, background-color",
|
|
38927
|
+
animation: "fadeIn 0.3s ease-in-out"
|
|
38928
|
+
},
|
|
38643
38929
|
children: [
|
|
38644
38930
|
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap group-hover:font-medium", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
38645
38931
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: index + 1 }),
|
|
38646
38932
|
getMedalIcon(index + 1)
|
|
38647
38933
|
] }) }),
|
|
38648
|
-
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children:
|
|
38649
|
-
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children:
|
|
38934
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.displayName }) }),
|
|
38935
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base whitespace-nowrap", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium", children: workspace.lineName }) }),
|
|
38650
38936
|
/* @__PURE__ */ jsxRuntime.jsxs("td", { className: "px-3 py-2.5 sm:p-4 text-sm sm:text-base font-medium whitespace-nowrap", children: [
|
|
38651
38937
|
(workspace.efficiency || 0).toFixed(1),
|
|
38652
38938
|
"%"
|
|
@@ -38664,7 +38950,9 @@ var DesktopWorkspaceRow = React21.memo(({
|
|
|
38664
38950
|
] })
|
|
38665
38951
|
]
|
|
38666
38952
|
}
|
|
38667
|
-
))
|
|
38953
|
+
), (prevProps, nextProps) => {
|
|
38954
|
+
return prevProps.index === nextProps.index && prevProps.rowClass === nextProps.rowClass && prevProps.workspace.workspace_uuid === nextProps.workspace.workspace_uuid && prevProps.workspace.efficiency === nextProps.workspace.efficiency && prevProps.workspace.action_count === nextProps.workspace.action_count && prevProps.workspace.action_threshold === nextProps.workspace.action_threshold && prevProps.workspace.avg_cycle_time === nextProps.workspace.avg_cycle_time && prevProps.workspace.displayName === nextProps.workspace.displayName && prevProps.workspace.lineName === nextProps.workspace.lineName && prevProps.onWorkspaceClick === nextProps.onWorkspaceClick && prevProps.getMedalIcon === nextProps.getMedalIcon;
|
|
38955
|
+
});
|
|
38668
38956
|
DesktopWorkspaceRow.displayName = "DesktopWorkspaceRow";
|
|
38669
38957
|
var LeaderboardDetailView = React21.memo(({
|
|
38670
38958
|
lineId,
|
|
@@ -38680,6 +38968,13 @@ var LeaderboardDetailView = React21.memo(({
|
|
|
38680
38968
|
const navigation = useNavigation();
|
|
38681
38969
|
const entityConfig = useEntityConfig();
|
|
38682
38970
|
const [sortAscending, setSortAscending] = React21.useState(false);
|
|
38971
|
+
const [isMobile, setIsMobile] = React21.useState(false);
|
|
38972
|
+
React21__namespace.default.useEffect(() => {
|
|
38973
|
+
const checkMobile = () => setIsMobile(window.innerWidth < 640);
|
|
38974
|
+
checkMobile();
|
|
38975
|
+
window.addEventListener("resize", checkMobile);
|
|
38976
|
+
return () => window.removeEventListener("resize", checkMobile);
|
|
38977
|
+
}, []);
|
|
38683
38978
|
const configuredLineNames = React21.useMemo(() => {
|
|
38684
38979
|
return getAllLineDisplayNames(entityConfig);
|
|
38685
38980
|
}, [entityConfig]);
|
|
@@ -38697,18 +38992,10 @@ var LeaderboardDetailView = React21.memo(({
|
|
|
38697
38992
|
const handleSortToggle = React21.useCallback(() => {
|
|
38698
38993
|
setSortAscending(!sortAscending);
|
|
38699
38994
|
}, [sortAscending]);
|
|
38700
|
-
const
|
|
38701
|
-
|
|
38702
|
-
|
|
38703
|
-
|
|
38704
|
-
}), [lineId, date, shift]);
|
|
38705
|
-
const {
|
|
38706
|
-
metrics: metrics2,
|
|
38707
|
-
lineDetails,
|
|
38708
|
-
loading: metricsLoading,
|
|
38709
|
-
error: metricsError,
|
|
38710
|
-
refreshMetrics
|
|
38711
|
-
} = useRealtimeLineMetrics(realtimeMetricsParams);
|
|
38995
|
+
const shiftId = React21.useMemo(
|
|
38996
|
+
() => typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0,
|
|
38997
|
+
[shift]
|
|
38998
|
+
);
|
|
38712
38999
|
const {
|
|
38713
39000
|
workspaces,
|
|
38714
39001
|
loading: workspacesLoading,
|
|
@@ -38718,12 +39005,12 @@ var LeaderboardDetailView = React21.memo(({
|
|
|
38718
39005
|
initialDate: date,
|
|
38719
39006
|
initialShiftId: typeof shift === "number" ? shift : typeof shift === "string" ? parseInt(shift) : void 0
|
|
38720
39007
|
});
|
|
38721
|
-
const getShiftName = React21.useCallback((
|
|
38722
|
-
if (
|
|
38723
|
-
return
|
|
39008
|
+
const getShiftName = React21.useCallback((shiftId2) => {
|
|
39009
|
+
if (shiftId2 === void 0) return "Day";
|
|
39010
|
+
return shiftId2 === 0 ? "Day" : "Night";
|
|
38724
39011
|
}, []);
|
|
38725
|
-
const getShiftIcon = React21.useCallback((
|
|
38726
|
-
const shift2 = getShiftName(
|
|
39012
|
+
const getShiftIcon = React21.useCallback((shiftId2) => {
|
|
39013
|
+
const shift2 = getShiftName(shiftId2);
|
|
38727
39014
|
if (shift2 === "Day") {
|
|
38728
39015
|
return /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" }) });
|
|
38729
39016
|
} else {
|
|
@@ -38765,17 +39052,18 @@ var LeaderboardDetailView = React21.memo(({
|
|
|
38765
39052
|
return null;
|
|
38766
39053
|
}
|
|
38767
39054
|
}, [sortAscending]);
|
|
39055
|
+
const workspacesLength = React21.useMemo(() => workspaces?.length || 0, [workspaces?.length]);
|
|
38768
39056
|
const handleWorkspaceClick = React21.useCallback((workspace, rank) => {
|
|
38769
39057
|
trackCoreEvent("Workspace from Leaderboard Clicked", {
|
|
38770
39058
|
workspace_name: workspace.workspace_name,
|
|
38771
39059
|
workspace_id: workspace.workspace_uuid,
|
|
38772
39060
|
rank,
|
|
38773
|
-
total_workspaces:
|
|
39061
|
+
total_workspaces: workspacesLength,
|
|
38774
39062
|
efficiency: workspace.efficiency,
|
|
38775
39063
|
action_count: workspace.action_count,
|
|
38776
39064
|
action_threshold: workspace.action_threshold
|
|
38777
39065
|
});
|
|
38778
|
-
const displayName = getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
39066
|
+
const displayName = workspace.displayName || getWorkspaceDisplayName(workspace.workspace_name, workspace.line_id);
|
|
38779
39067
|
const navParams = workspace.workspace_uuid ? getWorkspaceNavigationParams(workspace.workspace_uuid, displayName, workspace.line_id) : "";
|
|
38780
39068
|
const returnToParam = `&returnTo=${encodeURIComponent(`/leaderboard`)}`;
|
|
38781
39069
|
if (onWorkspaceClick) {
|
|
@@ -38783,20 +39071,33 @@ var LeaderboardDetailView = React21.memo(({
|
|
|
38783
39071
|
} else {
|
|
38784
39072
|
navigation.navigate(`/workspace/${workspace.workspace_uuid}${navParams}${returnToParam}`);
|
|
38785
39073
|
}
|
|
38786
|
-
}, [onWorkspaceClick, navigation, lineId,
|
|
38787
|
-
const
|
|
39074
|
+
}, [onWorkspaceClick, navigation, lineId, workspacesLength]);
|
|
39075
|
+
const workspaceDisplayData = React21.useMemo(() => {
|
|
38788
39076
|
if (!workspaces) return [];
|
|
38789
|
-
return
|
|
39077
|
+
return workspaces.map((ws) => ({
|
|
39078
|
+
...ws,
|
|
39079
|
+
displayName: getWorkspaceDisplayName(ws.workspace_name, ws.line_id),
|
|
39080
|
+
lineName: getLineName(ws.line_id)
|
|
39081
|
+
}));
|
|
39082
|
+
}, [workspaces, getLineName]);
|
|
39083
|
+
const sortedWorkspaces = React21.useMemo(() => {
|
|
39084
|
+
return [...workspaceDisplayData].sort((a, b) => {
|
|
38790
39085
|
const effA = a.efficiency || 0;
|
|
38791
39086
|
const effB = b.efficiency || 0;
|
|
38792
39087
|
return sortAscending ? effA - effB : effB - effA;
|
|
38793
39088
|
});
|
|
38794
|
-
}, [
|
|
38795
|
-
const loading =
|
|
38796
|
-
const error =
|
|
38797
|
-
const currentDateFormatted = React21.useMemo(() =>
|
|
38798
|
-
|
|
38799
|
-
|
|
39089
|
+
}, [workspaceDisplayData, sortAscending]);
|
|
39090
|
+
const loading = workspacesLoading;
|
|
39091
|
+
const error = workspacesError;
|
|
39092
|
+
const currentDateFormatted = React21.useMemo(() => {
|
|
39093
|
+
const dateStr = (/* @__PURE__ */ new Date()).toDateString();
|
|
39094
|
+
return formatDate(new Date(dateStr));
|
|
39095
|
+
}, [formatDate, date]);
|
|
39096
|
+
const currentMobileDateFormatted = React21.useMemo(() => {
|
|
39097
|
+
const dateStr = (/* @__PURE__ */ new Date()).toDateString();
|
|
39098
|
+
return formatMobileDate(new Date(dateStr));
|
|
39099
|
+
}, [formatMobileDate, date]);
|
|
39100
|
+
if (loading && (!workspaces || workspaces.length === 0)) {
|
|
38800
39101
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `h-[calc(100vh-64px)] flex items-center justify-center bg-slate-50 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xl text-gray-600", children: "Loading workspaces..." }) });
|
|
38801
39102
|
}
|
|
38802
39103
|
if (error) {
|
|
@@ -38805,7 +39106,7 @@ var LeaderboardDetailView = React21.memo(({
|
|
|
38805
39106
|
error.message
|
|
38806
39107
|
] }) });
|
|
38807
39108
|
}
|
|
38808
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, children: [
|
|
39109
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `min-h-screen bg-slate-50 flex flex-col ${className}`, style: { willChange: "contents" }, children: [
|
|
38809
39110
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-20 bg-white shadow-sm border-b border-gray-200/80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 sm:px-6 md:px-8 py-2 sm:py-2.5", children: [
|
|
38810
39111
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center", children: [
|
|
38811
39112
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -38897,31 +39198,30 @@ var LeaderboardDetailView = React21.memo(({
|
|
|
38897
39198
|
{
|
|
38898
39199
|
currentDate: currentDateFormatted,
|
|
38899
39200
|
currentMobileDate: currentMobileDateFormatted,
|
|
38900
|
-
shiftId
|
|
39201
|
+
shiftId,
|
|
38901
39202
|
getShiftIcon,
|
|
38902
39203
|
getShiftName
|
|
38903
39204
|
}
|
|
38904
39205
|
)
|
|
38905
39206
|
] }) }),
|
|
38906
|
-
/* @__PURE__ */ jsxRuntime.
|
|
38907
|
-
|
|
38908
|
-
|
|
38909
|
-
|
|
38910
|
-
|
|
38911
|
-
|
|
38912
|
-
|
|
38913
|
-
|
|
38914
|
-
|
|
38915
|
-
|
|
38916
|
-
|
|
38917
|
-
|
|
38918
|
-
|
|
38919
|
-
|
|
38920
|
-
|
|
38921
|
-
|
|
38922
|
-
|
|
38923
|
-
|
|
38924
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:block bg-white rounded-lg shadow-md overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse table-auto", children: [
|
|
39207
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 w-full mx-auto p-3 sm:p-4 md:p-6", children: isMobile ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: sortedWorkspaces.map((ws, index) => {
|
|
39208
|
+
const rank = index + 1;
|
|
39209
|
+
const isTopThree = index < 3;
|
|
39210
|
+
const cardClass = sortAscending ? isTopThree ? "bg-red-50/90 border-red-200" : "bg-white" : isTopThree ? "bg-green-50/90 border-green-200" : "bg-white";
|
|
39211
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
39212
|
+
MobileWorkspaceCard,
|
|
39213
|
+
{
|
|
39214
|
+
workspace: ws,
|
|
39215
|
+
rank,
|
|
39216
|
+
cardClass,
|
|
39217
|
+
onWorkspaceClick: handleWorkspaceClick,
|
|
39218
|
+
getMedalIcon
|
|
39219
|
+
},
|
|
39220
|
+
ws.workspace_uuid
|
|
39221
|
+
);
|
|
39222
|
+
}) }) : (
|
|
39223
|
+
/* Desktop table view - only render on desktop */
|
|
39224
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white rounded-lg shadow-md overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full border-collapse table-auto", children: [
|
|
38925
39225
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-gray-50 border-b border-gray-200 sticky top-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
38926
39226
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Rank" }),
|
|
38927
39227
|
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-3 py-2.5 sm:p-4 text-left text-xs sm:text-sm font-semibold text-gray-600 whitespace-nowrap", children: "Workspace" }),
|
|
@@ -38940,17 +39240,16 @@ var LeaderboardDetailView = React21.memo(({
|
|
|
38940
39240
|
index,
|
|
38941
39241
|
rowClass,
|
|
38942
39242
|
onWorkspaceClick: handleWorkspaceClick,
|
|
38943
|
-
getMedalIcon
|
|
38944
|
-
getLineName
|
|
39243
|
+
getMedalIcon
|
|
38945
39244
|
},
|
|
38946
39245
|
ws.workspace_uuid
|
|
38947
39246
|
);
|
|
38948
39247
|
}) })
|
|
38949
39248
|
] }) }) })
|
|
38950
|
-
|
|
39249
|
+
) })
|
|
38951
39250
|
] });
|
|
38952
39251
|
}, (prevProps, nextProps) => {
|
|
38953
|
-
return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && JSON.stringify(prevProps.lineNames) === JSON.stringify(nextProps.lineNames) && prevProps.className === nextProps.className
|
|
39252
|
+
return prevProps.lineId === nextProps.lineId && prevProps.date === nextProps.date && prevProps.shift === nextProps.shift && prevProps.line1Id === nextProps.line1Id && prevProps.line2Id === nextProps.line2Id && JSON.stringify(prevProps.lineNames) === JSON.stringify(nextProps.lineNames) && prevProps.className === nextProps.className;
|
|
38954
39253
|
});
|
|
38955
39254
|
LeaderboardDetailView.displayName = "LeaderboardDetailView";
|
|
38956
39255
|
var LeaderboardDetailViewWithDisplayNames = withAllWorkspaceDisplayNames(LeaderboardDetailView);
|
|
@@ -40059,16 +40358,8 @@ var ACTION_NAMES = {
|
|
|
40059
40358
|
// src/views/TargetsView.utils.ts
|
|
40060
40359
|
var calculatePPH = (cycleTime, breaks = [], shiftHours = 0) => {
|
|
40061
40360
|
if (cycleTime === "" || cycleTime === 0) return "";
|
|
40062
|
-
const
|
|
40063
|
-
|
|
40064
|
-
return Number(basicPPH.toFixed(1));
|
|
40065
|
-
}
|
|
40066
|
-
const safeBreaks = Array.isArray(breaks) ? breaks : [];
|
|
40067
|
-
const totalBreakMinutes = safeBreaks.reduce((total, breakItem) => total + breakItem.duration, 0);
|
|
40068
|
-
const totalBreakHours = totalBreakMinutes / 60;
|
|
40069
|
-
const realWorkHours = shiftHours - totalBreakHours;
|
|
40070
|
-
const effectivePPH = basicPPH * (realWorkHours / shiftHours);
|
|
40071
|
-
return Number(effectivePPH.toFixed(1));
|
|
40361
|
+
const pph = 3600 / cycleTime;
|
|
40362
|
+
return Number(pph.toFixed(1));
|
|
40072
40363
|
};
|
|
40073
40364
|
var calculateDayOutput = (pph, shiftHours, breaks = []) => {
|
|
40074
40365
|
if (pph === "") return "";
|
|
@@ -42664,7 +42955,7 @@ var WorkspaceDetailView = ({
|
|
|
42664
42955
|
/* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
|
|
42665
42956
|
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
42666
42957
|
/* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
42667
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
42958
|
+
/* @__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) }),
|
|
42668
42959
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
42669
42960
|
"Standard: ",
|
|
42670
42961
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -42784,7 +43075,7 @@ var WorkspaceDetailView = ({
|
|
|
42784
43075
|
/* @__PURE__ */ jsxRuntime.jsxs(Card2, { children: [
|
|
42785
43076
|
/* @__PURE__ */ jsxRuntime.jsx(CardHeader2, { className: "pb-2 flex-none", children: /* @__PURE__ */ jsxRuntime.jsx(CardTitle2, { className: "text-lg text-center", children: "Cycle Time (s)" }) }),
|
|
42786
43077
|
/* @__PURE__ */ jsxRuntime.jsx(CardContent2, { className: "flex-1 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
42787
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-5xl font-bold text-green-500`, children: workspace.avg_cycle_time.toFixed(1) }),
|
|
43078
|
+
/* @__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) }),
|
|
42788
43079
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 mt-2", children: [
|
|
42789
43080
|
"Standard: ",
|
|
42790
43081
|
workspace.ideal_cycle_time?.toFixed(1) || 0,
|
|
@@ -44124,6 +44415,7 @@ exports.AuthenticatedHomeView = AuthenticatedHomeView;
|
|
|
44124
44415
|
exports.AuthenticatedShiftsView = AuthenticatedShiftsView;
|
|
44125
44416
|
exports.AuthenticatedTargetsView = AuthenticatedTargetsView;
|
|
44126
44417
|
exports.AuthenticatedWorkspaceHealthView = AuthenticatedWorkspaceHealthView;
|
|
44418
|
+
exports.AxelNotificationPopup = AxelNotificationPopup;
|
|
44127
44419
|
exports.BackButton = BackButton;
|
|
44128
44420
|
exports.BackButtonMinimal = BackButtonMinimal;
|
|
44129
44421
|
exports.BarChart = BarChart;
|