@optifye/dashboard-core 4.1.1 → 4.1.2
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.d.mts +31 -2
- package/dist/index.d.ts +31 -2
- package/dist/index.js +443 -78
- package/dist/index.mjs +443 -78
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -25308,6 +25308,32 @@ var S3ClipsService = class {
|
|
|
25308
25308
|
return null;
|
|
25309
25309
|
}
|
|
25310
25310
|
}
|
|
25311
|
+
/**
|
|
25312
|
+
* Fetches full metadata including timestamps
|
|
25313
|
+
*/
|
|
25314
|
+
async getFullMetadata(playlistUri) {
|
|
25315
|
+
try {
|
|
25316
|
+
const metadataUri = playlistUri.replace(/playlist\.m3u8$/, "metadata.json");
|
|
25317
|
+
const url = new URL(metadataUri);
|
|
25318
|
+
const bucket = url.hostname;
|
|
25319
|
+
const key = url.pathname.substring(1);
|
|
25320
|
+
const command = new GetObjectCommand({
|
|
25321
|
+
Bucket: bucket,
|
|
25322
|
+
Key: key
|
|
25323
|
+
});
|
|
25324
|
+
const response = await this.s3Client.send(command);
|
|
25325
|
+
if (!response.Body) {
|
|
25326
|
+
console.warn(`Empty response body for metadata file: ${key}`);
|
|
25327
|
+
return null;
|
|
25328
|
+
}
|
|
25329
|
+
const metadataContent = await response.Body.transformToString();
|
|
25330
|
+
const metadata = JSON.parse(metadataContent);
|
|
25331
|
+
return metadata;
|
|
25332
|
+
} catch (error) {
|
|
25333
|
+
console.error(`Error fetching or parsing metadata:`, error);
|
|
25334
|
+
return null;
|
|
25335
|
+
}
|
|
25336
|
+
}
|
|
25311
25337
|
/**
|
|
25312
25338
|
* Converts S3 URI to CloudFront URL
|
|
25313
25339
|
* Uses streaming proxy for localhost development to handle CORS
|
|
@@ -25320,15 +25346,22 @@ var S3ClipsService = class {
|
|
|
25320
25346
|
/**
|
|
25321
25347
|
* Processes a single video completely
|
|
25322
25348
|
*/
|
|
25323
|
-
async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime) {
|
|
25349
|
+
async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
|
|
25324
25350
|
const parsedInfo = parseS3Uri(uri);
|
|
25325
25351
|
if (!parsedInfo) {
|
|
25326
25352
|
console.warn(`Skipping URI due to parsing failure: ${uri}`);
|
|
25327
25353
|
return null;
|
|
25328
25354
|
}
|
|
25329
25355
|
let cycleTimeSeconds = null;
|
|
25330
|
-
|
|
25331
|
-
|
|
25356
|
+
let creationTimestamp = void 0;
|
|
25357
|
+
if (includeMetadata || includeCycleTime && (parsedInfo.type === "bottleneck" && parsedInfo.description.toLowerCase().includes("cycle time") || parsedInfo.type === "best_cycle_time" || parsedInfo.type === "worst_cycle_time")) {
|
|
25358
|
+
const metadata = await this.getFullMetadata(uri);
|
|
25359
|
+
if (metadata) {
|
|
25360
|
+
if (metadata.original_task_metadata?.cycle_time) {
|
|
25361
|
+
cycleTimeSeconds = metadata.original_task_metadata.cycle_time;
|
|
25362
|
+
}
|
|
25363
|
+
creationTimestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp || metadata[""];
|
|
25364
|
+
}
|
|
25332
25365
|
}
|
|
25333
25366
|
const cloudfrontPlaylistUrl = this.s3UriToCloudfront(uri);
|
|
25334
25367
|
const { type: videoType, timestamp: videoTimestamp } = parsedInfo;
|
|
@@ -25337,7 +25370,8 @@ var S3ClipsService = class {
|
|
|
25337
25370
|
src: cloudfrontPlaylistUrl,
|
|
25338
25371
|
// Direct CloudFront playlist URL
|
|
25339
25372
|
...parsedInfo,
|
|
25340
|
-
cycle_time_seconds: cycleTimeSeconds || void 0
|
|
25373
|
+
cycle_time_seconds: cycleTimeSeconds || void 0,
|
|
25374
|
+
creation_timestamp: creationTimestamp
|
|
25341
25375
|
};
|
|
25342
25376
|
}
|
|
25343
25377
|
/**
|
|
@@ -25399,7 +25433,7 @@ var S3ClipsService = class {
|
|
|
25399
25433
|
* Main method to fetch clips based on parameters
|
|
25400
25434
|
*/
|
|
25401
25435
|
async fetchClips(params) {
|
|
25402
|
-
const { workspaceId, date: inputDate, shift, category, limit, mode, includeCycleTime } = params;
|
|
25436
|
+
const { workspaceId, date: inputDate, shift, category, limit, mode, includeCycleTime, includeMetadata, timestampStart, timestampEnd } = params;
|
|
25403
25437
|
if (!workspaceId) {
|
|
25404
25438
|
throw new Error("Valid Workspace ID is required");
|
|
25405
25439
|
}
|
|
@@ -25504,7 +25538,7 @@ var S3ClipsService = class {
|
|
|
25504
25538
|
const batch = filteredUris.slice(i, i + concurrencyLimit);
|
|
25505
25539
|
const batchPromises = batch.map(async (uri, batchIndex) => {
|
|
25506
25540
|
const index = i + batchIndex;
|
|
25507
|
-
const result = await this.processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime || false);
|
|
25541
|
+
const result = await this.processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime || false, includeMetadata || (!!timestampStart || !!timestampEnd));
|
|
25508
25542
|
processedCount++;
|
|
25509
25543
|
if (processedCount % 10 === 0) {
|
|
25510
25544
|
console.log(`S3ClipsService: Processed ${processedCount}/${filteredUris.length} videos for category '${category || "all"}'...`);
|
|
@@ -25514,7 +25548,26 @@ var S3ClipsService = class {
|
|
|
25514
25548
|
const batchResults = await Promise.all(batchPromises);
|
|
25515
25549
|
videoResults.push(...batchResults);
|
|
25516
25550
|
}
|
|
25517
|
-
|
|
25551
|
+
let videos = videoResults.filter((v) => v !== null);
|
|
25552
|
+
if (timestampStart || timestampEnd) {
|
|
25553
|
+
videos = videos.filter((video) => {
|
|
25554
|
+
if (!video.creation_timestamp) return false;
|
|
25555
|
+
const videoTimestamp = new Date(video.creation_timestamp).getTime();
|
|
25556
|
+
if (timestampStart && timestampEnd) {
|
|
25557
|
+
const start = new Date(timestampStart).getTime();
|
|
25558
|
+
const end = new Date(timestampEnd).getTime();
|
|
25559
|
+
return videoTimestamp >= start && videoTimestamp <= end;
|
|
25560
|
+
} else if (timestampStart) {
|
|
25561
|
+
const start = new Date(timestampStart).getTime();
|
|
25562
|
+
return videoTimestamp >= start;
|
|
25563
|
+
} else if (timestampEnd) {
|
|
25564
|
+
const end = new Date(timestampEnd).getTime();
|
|
25565
|
+
return videoTimestamp <= end;
|
|
25566
|
+
}
|
|
25567
|
+
return true;
|
|
25568
|
+
});
|
|
25569
|
+
console.log(`S3ClipsService: Filtered by timestamp - ${videos.length} videos remain after filtering`);
|
|
25570
|
+
}
|
|
25518
25571
|
console.log(`S3ClipsService: Successfully processed ${videos.length} out of ${filteredUris.length} video clips for category '${category || "all"}' (limit: ${limitPerCategory} per category).`);
|
|
25519
25572
|
const typeCounts = videos.reduce((acc, video) => {
|
|
25520
25573
|
acc[video.type] = (acc[video.type] || 0) + 1;
|
|
@@ -25559,6 +25612,7 @@ var BottlenecksContent = ({
|
|
|
25559
25612
|
const dashboardConfig = useDashboardConfig();
|
|
25560
25613
|
const videoRef = useRef(null);
|
|
25561
25614
|
const fullscreenContainerRef = useRef(null);
|
|
25615
|
+
const timestampFilterRef = useRef(null);
|
|
25562
25616
|
const [isPlaying, setIsPlaying] = useState(false);
|
|
25563
25617
|
const [currentTime, setCurrentTime] = useState(0);
|
|
25564
25618
|
const [duration, setDuration] = useState(0);
|
|
@@ -25568,6 +25622,22 @@ var BottlenecksContent = ({
|
|
|
25568
25622
|
const [allVideos, setAllVideos] = useState([]);
|
|
25569
25623
|
const [isLoading, setIsLoading] = useState(true);
|
|
25570
25624
|
const [error, setError] = useState(null);
|
|
25625
|
+
const [showTimestampFilter, setShowTimestampFilter] = useState(false);
|
|
25626
|
+
const [timestampStart, setTimestampStart] = useState("");
|
|
25627
|
+
const [timestampEnd, setTimestampEnd] = useState("");
|
|
25628
|
+
useEffect(() => {
|
|
25629
|
+
const handleClickOutside = (event) => {
|
|
25630
|
+
if (timestampFilterRef.current && !timestampFilterRef.current.contains(event.target)) {
|
|
25631
|
+
setShowTimestampFilter(false);
|
|
25632
|
+
}
|
|
25633
|
+
};
|
|
25634
|
+
if (showTimestampFilter) {
|
|
25635
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
25636
|
+
}
|
|
25637
|
+
return () => {
|
|
25638
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
25639
|
+
};
|
|
25640
|
+
}, [showTimestampFilter]);
|
|
25571
25641
|
const s3ClipsService = useMemo(() => {
|
|
25572
25642
|
if (!dashboardConfig?.s3Config) {
|
|
25573
25643
|
console.warn("S3 configuration not found in dashboard config");
|
|
@@ -25580,13 +25650,26 @@ var BottlenecksContent = ({
|
|
|
25580
25650
|
setIsLoading(true);
|
|
25581
25651
|
setError(null);
|
|
25582
25652
|
try {
|
|
25653
|
+
const operationalDate = date || getOperationalDate();
|
|
25654
|
+
let timestampStartFull;
|
|
25655
|
+
let timestampEndFull;
|
|
25656
|
+
if (timestampStart) {
|
|
25657
|
+
timestampStartFull = `${operationalDate}T${timestampStart}:00`;
|
|
25658
|
+
}
|
|
25659
|
+
if (timestampEnd) {
|
|
25660
|
+
timestampEndFull = `${operationalDate}T${timestampEnd}:00`;
|
|
25661
|
+
}
|
|
25583
25662
|
const videos = await s3ClipsService.fetchClips({
|
|
25584
25663
|
workspaceId,
|
|
25585
|
-
date:
|
|
25664
|
+
date: operationalDate,
|
|
25586
25665
|
mode: "full",
|
|
25587
25666
|
includeCycleTime: true,
|
|
25588
|
-
|
|
25667
|
+
includeMetadata: true,
|
|
25668
|
+
// Always include metadata for timestamp info
|
|
25669
|
+
limit: 50,
|
|
25589
25670
|
// Reasonable limit for UI performance
|
|
25671
|
+
timestampStart: timestampStartFull,
|
|
25672
|
+
timestampEnd: timestampEndFull
|
|
25590
25673
|
});
|
|
25591
25674
|
if (Array.isArray(videos) && videos.length > 0) {
|
|
25592
25675
|
preloadVideoUrl2(videos[0].src);
|
|
@@ -25614,7 +25697,7 @@ var BottlenecksContent = ({
|
|
|
25614
25697
|
} finally {
|
|
25615
25698
|
setIsLoading(false);
|
|
25616
25699
|
}
|
|
25617
|
-
}, [workspaceId, date, s3ClipsService]);
|
|
25700
|
+
}, [workspaceId, date, s3ClipsService, timestampStart, timestampEnd]);
|
|
25618
25701
|
useEffect(() => {
|
|
25619
25702
|
if (s3ClipsService) {
|
|
25620
25703
|
fetchClips();
|
|
@@ -25983,6 +26066,43 @@ var BottlenecksContent = ({
|
|
|
25983
26066
|
return "Bottleneck";
|
|
25984
26067
|
}
|
|
25985
26068
|
};
|
|
26069
|
+
const formatTimestamp = (timestamp) => {
|
|
26070
|
+
if (!timestamp) return "";
|
|
26071
|
+
try {
|
|
26072
|
+
const date2 = new Date(timestamp);
|
|
26073
|
+
const today = /* @__PURE__ */ new Date();
|
|
26074
|
+
const isToday = date2.toDateString() === today.toDateString();
|
|
26075
|
+
if (isToday) {
|
|
26076
|
+
return date2.toLocaleString("en-US", {
|
|
26077
|
+
hour: "numeric",
|
|
26078
|
+
minute: "2-digit",
|
|
26079
|
+
hour12: true
|
|
26080
|
+
});
|
|
26081
|
+
} else {
|
|
26082
|
+
return date2.toLocaleString("en-US", {
|
|
26083
|
+
month: "short",
|
|
26084
|
+
day: "numeric",
|
|
26085
|
+
hour: "numeric",
|
|
26086
|
+
minute: "2-digit",
|
|
26087
|
+
hour12: true
|
|
26088
|
+
});
|
|
26089
|
+
}
|
|
26090
|
+
} catch {
|
|
26091
|
+
return "";
|
|
26092
|
+
}
|
|
26093
|
+
};
|
|
26094
|
+
const formatTimeOnly = (time2) => {
|
|
26095
|
+
if (!time2) return "";
|
|
26096
|
+
try {
|
|
26097
|
+
const [hours, minutes] = time2.split(":");
|
|
26098
|
+
const hour = parseInt(hours);
|
|
26099
|
+
const ampm = hour >= 12 ? "PM" : "AM";
|
|
26100
|
+
const displayHour = hour % 12 || 12;
|
|
26101
|
+
return `${displayHour}:${minutes} ${ampm}`;
|
|
26102
|
+
} catch {
|
|
26103
|
+
return time2;
|
|
26104
|
+
}
|
|
26105
|
+
};
|
|
25986
26106
|
if (!dashboardConfig?.s3Config) {
|
|
25987
26107
|
return /* @__PURE__ */ jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
|
|
25988
26108
|
/* @__PURE__ */ jsx(XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
|
|
@@ -26091,6 +26211,74 @@ var BottlenecksContent = ({
|
|
|
26091
26211
|
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 border-b border-gray-100", children: /* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
26092
26212
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-gray-800", children: activeFilter === "low_value" ? `Low Value Moments (${clipCounts.lowValue})` : activeFilter === "best_cycle_time" ? `Best Cycle Times (${clipCounts.bestCycleTimes})` : activeFilter === "worst_cycle_time" ? `Worst Cycle Times (${clipCounts.worstCycleTimes})` : activeFilter === "long_cycle_time" ? `Long Cycle Time (${clipCounts.longCycleTimes})` : `All Clips (${clipCounts.total})` }),
|
|
26093
26213
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2", children: [
|
|
26214
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", ref: timestampFilterRef, children: [
|
|
26215
|
+
/* @__PURE__ */ jsx(
|
|
26216
|
+
"button",
|
|
26217
|
+
{
|
|
26218
|
+
onClick: () => setShowTimestampFilter(!showTimestampFilter),
|
|
26219
|
+
className: `p-2 rounded-lg transition-all ${timestampStart || timestampEnd ? "bg-blue-50 text-blue-600 hover:bg-blue-100" : "text-gray-600 hover:bg-gray-100"}`,
|
|
26220
|
+
"aria-label": "Filter by time",
|
|
26221
|
+
title: "Filter by time",
|
|
26222
|
+
children: /* @__PURE__ */ jsx(Clock, { className: "h-5 w-5" })
|
|
26223
|
+
}
|
|
26224
|
+
),
|
|
26225
|
+
showTimestampFilter && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 mt-2 w-80 bg-white rounded-lg shadow-lg border border-gray-200 p-4 z-20", children: [
|
|
26226
|
+
/* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold text-gray-700 mb-3", children: "Filter by Time" }),
|
|
26227
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
26228
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
26229
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "timestamp-start", className: "block text-xs font-medium text-gray-600 mb-1", children: "Start Time" }),
|
|
26230
|
+
/* @__PURE__ */ jsx(
|
|
26231
|
+
"input",
|
|
26232
|
+
{
|
|
26233
|
+
id: "timestamp-start",
|
|
26234
|
+
type: "time",
|
|
26235
|
+
value: timestampStart,
|
|
26236
|
+
onChange: (e) => setTimestampStart(e.target.value),
|
|
26237
|
+
className: "w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
26238
|
+
}
|
|
26239
|
+
)
|
|
26240
|
+
] }),
|
|
26241
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
26242
|
+
/* @__PURE__ */ jsx("label", { htmlFor: "timestamp-end", className: "block text-xs font-medium text-gray-600 mb-1", children: "End Time" }),
|
|
26243
|
+
/* @__PURE__ */ jsx(
|
|
26244
|
+
"input",
|
|
26245
|
+
{
|
|
26246
|
+
id: "timestamp-end",
|
|
26247
|
+
type: "time",
|
|
26248
|
+
value: timestampEnd,
|
|
26249
|
+
onChange: (e) => setTimestampEnd(e.target.value),
|
|
26250
|
+
className: "w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
26251
|
+
}
|
|
26252
|
+
)
|
|
26253
|
+
] }),
|
|
26254
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between pt-2", children: [
|
|
26255
|
+
/* @__PURE__ */ jsx(
|
|
26256
|
+
"button",
|
|
26257
|
+
{
|
|
26258
|
+
onClick: () => {
|
|
26259
|
+
setTimestampStart("");
|
|
26260
|
+
setTimestampEnd("");
|
|
26261
|
+
setShowTimestampFilter(false);
|
|
26262
|
+
},
|
|
26263
|
+
className: "px-3 py-1.5 text-sm text-gray-600 hover:text-gray-800 transition-colors",
|
|
26264
|
+
children: "Clear"
|
|
26265
|
+
}
|
|
26266
|
+
),
|
|
26267
|
+
/* @__PURE__ */ jsx(
|
|
26268
|
+
"button",
|
|
26269
|
+
{
|
|
26270
|
+
onClick: () => {
|
|
26271
|
+
setShowTimestampFilter(false);
|
|
26272
|
+
fetchClips();
|
|
26273
|
+
},
|
|
26274
|
+
className: "px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors",
|
|
26275
|
+
children: "Apply Filter"
|
|
26276
|
+
}
|
|
26277
|
+
)
|
|
26278
|
+
] })
|
|
26279
|
+
] })
|
|
26280
|
+
] })
|
|
26281
|
+
] }),
|
|
26094
26282
|
/* @__PURE__ */ jsx(
|
|
26095
26283
|
"button",
|
|
26096
26284
|
{
|
|
@@ -26114,6 +26302,29 @@ var BottlenecksContent = ({
|
|
|
26114
26302
|
)
|
|
26115
26303
|
] })
|
|
26116
26304
|
] }) }),
|
|
26305
|
+
(timestampStart || timestampEnd) && /* @__PURE__ */ jsxs("div", { className: "px-4 py-2 bg-blue-50 border-b border-blue-100 flex items-center justify-between", children: [
|
|
26306
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center space-x-2 text-sm text-blue-700", children: [
|
|
26307
|
+
/* @__PURE__ */ jsx(Clock, { className: "h-4 w-4" }),
|
|
26308
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
26309
|
+
"Filtered by time: ",
|
|
26310
|
+
timestampStart ? formatTimeOnly(timestampStart) : "Any",
|
|
26311
|
+
" - ",
|
|
26312
|
+
timestampEnd ? formatTimeOnly(timestampEnd) : "Any"
|
|
26313
|
+
] })
|
|
26314
|
+
] }),
|
|
26315
|
+
/* @__PURE__ */ jsx(
|
|
26316
|
+
"button",
|
|
26317
|
+
{
|
|
26318
|
+
onClick: () => {
|
|
26319
|
+
setTimestampStart("");
|
|
26320
|
+
setTimestampEnd("");
|
|
26321
|
+
fetchClips();
|
|
26322
|
+
},
|
|
26323
|
+
className: "text-sm text-blue-600 hover:text-blue-800 transition-colors",
|
|
26324
|
+
children: "Clear filter"
|
|
26325
|
+
}
|
|
26326
|
+
)
|
|
26327
|
+
] }),
|
|
26117
26328
|
isLoading && allVideos.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsx(LoadingSpinner2, { size: "md", message: "Loading clips..." }) }) : allVideos.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxs("div", { className: "text-center p-8", children: [
|
|
26118
26329
|
/* @__PURE__ */ jsx("svg", { className: "w-16 h-16 text-gray-300 mx-auto mb-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" }) }),
|
|
26119
26330
|
/* @__PURE__ */ jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
|
|
@@ -26173,6 +26384,7 @@ var BottlenecksContent = ({
|
|
|
26173
26384
|
/* @__PURE__ */ jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
|
|
26174
26385
|
] }) })
|
|
26175
26386
|
),
|
|
26387
|
+
currentVideo.creation_timestamp && /* @__PURE__ */ jsx("div", { className: "absolute bottom-3 left-3 z-10 bg-black/50 backdrop-blur-sm px-2 py-1 rounded text-white shadow-sm text-xs", children: /* @__PURE__ */ jsx("span", { className: "opacity-80", children: formatTimestamp(currentVideo.creation_timestamp) }) }),
|
|
26176
26388
|
/* @__PURE__ */ jsx("div", { className: "absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-white", children: [
|
|
26177
26389
|
/* @__PURE__ */ jsx(
|
|
26178
26390
|
"button",
|
|
@@ -28721,6 +28933,23 @@ var ThreadSidebar = ({
|
|
|
28721
28933
|
] });
|
|
28722
28934
|
};
|
|
28723
28935
|
var axelProfilePng = "/axel-profile.png";
|
|
28936
|
+
var ProfilePicture = React46__default.memo(({ alt = "Axel", className = "w-12 h-12" }) => {
|
|
28937
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: `${className} rounded-xl overflow-hidden shadow-sm`, children: /* @__PURE__ */ jsx(
|
|
28938
|
+
"img",
|
|
28939
|
+
{
|
|
28940
|
+
src: axelProfilePng,
|
|
28941
|
+
alt,
|
|
28942
|
+
className: "w-full h-full object-cover",
|
|
28943
|
+
loading: "eager",
|
|
28944
|
+
decoding: "async"
|
|
28945
|
+
}
|
|
28946
|
+
) }) });
|
|
28947
|
+
});
|
|
28948
|
+
ProfilePicture.displayName = "ProfilePicture";
|
|
28949
|
+
var preloadImage = (src) => {
|
|
28950
|
+
const img = new Image();
|
|
28951
|
+
img.src = src;
|
|
28952
|
+
};
|
|
28724
28953
|
var AIAgentView = () => {
|
|
28725
28954
|
const { navigate, pathname } = useNavigation();
|
|
28726
28955
|
const config = useDashboardConfig();
|
|
@@ -28739,12 +28968,85 @@ var AIAgentView = () => {
|
|
|
28739
28968
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
28740
28969
|
const [typedText, setTypedText] = useState("");
|
|
28741
28970
|
const [newChatCount, setNewChatCount] = useState(0);
|
|
28971
|
+
const [hasStartedTyping, setHasStartedTyping] = useState(false);
|
|
28972
|
+
const [typingStartTime, setTypingStartTime] = useState(null);
|
|
28973
|
+
const [lastTypingTime, setLastTypingTime] = useState(null);
|
|
28974
|
+
const [characterCount, setCharacterCount] = useState(0);
|
|
28975
|
+
const typingTimeoutRef = useRef(null);
|
|
28742
28976
|
const isThreadLoading = (threadId) => {
|
|
28743
28977
|
return threadId ? loadingThreads.has(threadId) : false;
|
|
28744
28978
|
};
|
|
28745
28979
|
const getStreamingState = (threadId) => {
|
|
28746
28980
|
return threadId ? streamingStates.get(threadId) || { message: "", reasoning: "" } : { message: "", reasoning: "" };
|
|
28747
28981
|
};
|
|
28982
|
+
const trackTypingStart = () => {
|
|
28983
|
+
if (!hasStartedTyping) {
|
|
28984
|
+
const now2 = Date.now();
|
|
28985
|
+
setHasStartedTyping(true);
|
|
28986
|
+
setTypingStartTime(now2);
|
|
28987
|
+
setLastTypingTime(now2);
|
|
28988
|
+
trackCoreEvent("AI Agent Input Started", {
|
|
28989
|
+
line_id: lineId,
|
|
28990
|
+
company_id: companyId,
|
|
28991
|
+
shift_id: shiftId,
|
|
28992
|
+
active_thread_id: activeThreadId,
|
|
28993
|
+
has_existing_messages: messages.length > 0,
|
|
28994
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
28995
|
+
});
|
|
28996
|
+
}
|
|
28997
|
+
};
|
|
28998
|
+
const trackTypingProgress = (newValue) => {
|
|
28999
|
+
const now2 = Date.now();
|
|
29000
|
+
setLastTypingTime(now2);
|
|
29001
|
+
setCharacterCount(newValue.length);
|
|
29002
|
+
if (typingTimeoutRef.current) {
|
|
29003
|
+
clearTimeout(typingTimeoutRef.current);
|
|
29004
|
+
}
|
|
29005
|
+
typingTimeoutRef.current = setTimeout(() => {
|
|
29006
|
+
if (hasStartedTyping && typingStartTime && newValue.length > 0) {
|
|
29007
|
+
const typingDuration = now2 - typingStartTime;
|
|
29008
|
+
trackCoreEvent("AI Agent Input Typing Progress", {
|
|
29009
|
+
line_id: lineId,
|
|
29010
|
+
company_id: companyId,
|
|
29011
|
+
shift_id: shiftId,
|
|
29012
|
+
active_thread_id: activeThreadId,
|
|
29013
|
+
character_count: newValue.length,
|
|
29014
|
+
typing_duration_ms: typingDuration,
|
|
29015
|
+
has_existing_messages: messages.length > 0,
|
|
29016
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
29017
|
+
});
|
|
29018
|
+
}
|
|
29019
|
+
}, 2e3);
|
|
29020
|
+
};
|
|
29021
|
+
const trackMessageSent = (messageContent) => {
|
|
29022
|
+
if (hasStartedTyping && typingStartTime) {
|
|
29023
|
+
const now2 = Date.now();
|
|
29024
|
+
const totalTypingDuration = now2 - typingStartTime;
|
|
29025
|
+
trackCoreEvent("AI Agent Message Sent", {
|
|
29026
|
+
line_id: lineId,
|
|
29027
|
+
company_id: companyId,
|
|
29028
|
+
shift_id: shiftId,
|
|
29029
|
+
active_thread_id: activeThreadId,
|
|
29030
|
+
message_length: messageContent.length,
|
|
29031
|
+
character_count: messageContent.length,
|
|
29032
|
+
typing_duration_ms: totalTypingDuration,
|
|
29033
|
+
has_existing_messages: messages.length > 0,
|
|
29034
|
+
is_new_conversation: !activeThreadId,
|
|
29035
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
29036
|
+
});
|
|
29037
|
+
}
|
|
29038
|
+
resetTypingState();
|
|
29039
|
+
};
|
|
29040
|
+
const resetTypingState = () => {
|
|
29041
|
+
setHasStartedTyping(false);
|
|
29042
|
+
setTypingStartTime(null);
|
|
29043
|
+
setLastTypingTime(null);
|
|
29044
|
+
setCharacterCount(0);
|
|
29045
|
+
if (typingTimeoutRef.current) {
|
|
29046
|
+
clearTimeout(typingTimeoutRef.current);
|
|
29047
|
+
typingTimeoutRef.current = null;
|
|
29048
|
+
}
|
|
29049
|
+
};
|
|
28748
29050
|
const textareaRef = useRef(null);
|
|
28749
29051
|
const messagesEndRef = useRef(null);
|
|
28750
29052
|
const containerRef = useRef(null);
|
|
@@ -28767,7 +29069,7 @@ var AIAgentView = () => {
|
|
|
28767
29069
|
const { shiftId } = getCurrentShift(dateTimeConfig.defaultTimezone || "Asia/Kolkata", shiftConfig);
|
|
28768
29070
|
const companyId = entityConfig.companyId || "default-company-id";
|
|
28769
29071
|
const ACTIVE_THREAD_STORAGE_KEY = `ai-agent-active-thread-${lineId}`;
|
|
28770
|
-
|
|
29072
|
+
useLayoutEffect(() => {
|
|
28771
29073
|
const savedThreadId = localStorage.getItem(ACTIVE_THREAD_STORAGE_KEY);
|
|
28772
29074
|
if (savedThreadId && savedThreadId !== "undefined") {
|
|
28773
29075
|
setActiveThreadId(savedThreadId);
|
|
@@ -28780,6 +29082,27 @@ var AIAgentView = () => {
|
|
|
28780
29082
|
localStorage.removeItem(ACTIVE_THREAD_STORAGE_KEY);
|
|
28781
29083
|
}
|
|
28782
29084
|
}, [activeThreadId, ACTIVE_THREAD_STORAGE_KEY]);
|
|
29085
|
+
useEffect(() => {
|
|
29086
|
+
const handleVisibilityChange = () => {
|
|
29087
|
+
if (document.visibilityState === "hidden" && activeThreadId) {
|
|
29088
|
+
localStorage.setItem(ACTIVE_THREAD_STORAGE_KEY, activeThreadId);
|
|
29089
|
+
}
|
|
29090
|
+
};
|
|
29091
|
+
const handleBeforeUnload = () => {
|
|
29092
|
+
if (activeThreadId) {
|
|
29093
|
+
localStorage.setItem(ACTIVE_THREAD_STORAGE_KEY, activeThreadId);
|
|
29094
|
+
}
|
|
29095
|
+
};
|
|
29096
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
29097
|
+
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
29098
|
+
return () => {
|
|
29099
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
29100
|
+
window.removeEventListener("beforeunload", handleBeforeUnload);
|
|
29101
|
+
if (activeThreadId) {
|
|
29102
|
+
localStorage.setItem(ACTIVE_THREAD_STORAGE_KEY, activeThreadId);
|
|
29103
|
+
}
|
|
29104
|
+
};
|
|
29105
|
+
}, [activeThreadId, ACTIVE_THREAD_STORAGE_KEY]);
|
|
28783
29106
|
useEffect(() => {
|
|
28784
29107
|
if (textareaRef.current) {
|
|
28785
29108
|
textareaRef.current.style.height = "auto";
|
|
@@ -28830,9 +29153,20 @@ var AIAgentView = () => {
|
|
|
28830
29153
|
setInputValue("");
|
|
28831
29154
|
setPendingThreadId(null);
|
|
28832
29155
|
setTypedText("");
|
|
29156
|
+
resetTypingState();
|
|
28833
29157
|
localStorage.removeItem(ACTIVE_THREAD_STORAGE_KEY);
|
|
28834
29158
|
textareaRef.current?.focus();
|
|
28835
29159
|
};
|
|
29160
|
+
useEffect(() => {
|
|
29161
|
+
preloadImage(axelProfilePng);
|
|
29162
|
+
}, []);
|
|
29163
|
+
useEffect(() => {
|
|
29164
|
+
return () => {
|
|
29165
|
+
if (typingTimeoutRef.current) {
|
|
29166
|
+
clearTimeout(typingTimeoutRef.current);
|
|
29167
|
+
}
|
|
29168
|
+
};
|
|
29169
|
+
}, []);
|
|
28836
29170
|
useEffect(() => {
|
|
28837
29171
|
const checkAuth = async () => {
|
|
28838
29172
|
const supabase2 = _getSupabaseInstance();
|
|
@@ -28863,6 +29197,7 @@ var AIAgentView = () => {
|
|
|
28863
29197
|
let currentThreadId = activeThreadId || `temp-${Date.now()}`;
|
|
28864
29198
|
if (isThreadLoading(currentThreadId)) return;
|
|
28865
29199
|
const userMessage = inputValue.trim();
|
|
29200
|
+
trackMessageSent(userMessage);
|
|
28866
29201
|
if (displayMessages.length === 0) {
|
|
28867
29202
|
setIsTransitioning(true);
|
|
28868
29203
|
setTimeout(() => {
|
|
@@ -29270,18 +29605,11 @@ var AIAgentView = () => {
|
|
|
29270
29605
|
{
|
|
29271
29606
|
ref: containerRef,
|
|
29272
29607
|
className: `flex-1 bg-gray-50/50 min-h-0 ${displayMessages.length === 0 && !isTransitioning ? "flex items-center justify-center" : "overflow-y-auto"}`,
|
|
29273
|
-
children: displayMessages.length === 0 && !isTransitioning ? (
|
|
29608
|
+
children: !activeThreadId && displayMessages.length === 0 && !isTransitioning ? (
|
|
29274
29609
|
/* Centered welcome and input for new chat */
|
|
29275
29610
|
/* @__PURE__ */ jsxs("div", { className: "w-full max-w-3xl mx-auto px-4 sm:px-6 flex flex-col items-center justify-center space-y-12 -mt-16", children: [
|
|
29276
29611
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
29277
|
-
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center mb-8", children: /* @__PURE__ */ jsx(
|
|
29278
|
-
"img",
|
|
29279
|
-
{
|
|
29280
|
-
src: axelProfilePng,
|
|
29281
|
-
alt: "Axel - AI Manufacturing Expert",
|
|
29282
|
-
className: "w-full h-full object-cover"
|
|
29283
|
-
}
|
|
29284
|
-
) }) }),
|
|
29612
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-center mb-8", children: /* @__PURE__ */ jsx(ProfilePicture, { alt: "Axel - AI Manufacturing Expert", className: "w-24 h-24" }) }),
|
|
29285
29613
|
/* @__PURE__ */ jsxs("h2", { className: "text-3xl font-semibold text-gray-900", children: [
|
|
29286
29614
|
typedText,
|
|
29287
29615
|
typedText.length < "Hi, I'm Axel - Your AI Supervisor".length && /* @__PURE__ */ jsx("span", { className: "animate-pulse", children: "|" })
|
|
@@ -29306,8 +29634,30 @@ var AIAgentView = () => {
|
|
|
29306
29634
|
{
|
|
29307
29635
|
ref: textareaRef,
|
|
29308
29636
|
value: inputValue,
|
|
29309
|
-
onChange: (e) =>
|
|
29637
|
+
onChange: (e) => {
|
|
29638
|
+
const newValue = e.target.value;
|
|
29639
|
+
setInputValue(newValue);
|
|
29640
|
+
if (newValue.length > 0 && !hasStartedTyping) {
|
|
29641
|
+
trackTypingStart();
|
|
29642
|
+
}
|
|
29643
|
+
if (newValue.length > 0) {
|
|
29644
|
+
trackTypingProgress(newValue);
|
|
29645
|
+
}
|
|
29646
|
+
if (newValue.length === 0) {
|
|
29647
|
+
resetTypingState();
|
|
29648
|
+
}
|
|
29649
|
+
},
|
|
29310
29650
|
onKeyDown: handleKeyDown,
|
|
29651
|
+
onFocus: () => {
|
|
29652
|
+
trackCoreEvent("AI Agent Input Focused", {
|
|
29653
|
+
line_id: lineId,
|
|
29654
|
+
company_id: companyId,
|
|
29655
|
+
shift_id: shiftId,
|
|
29656
|
+
active_thread_id: activeThreadId,
|
|
29657
|
+
has_existing_messages: messages.length > 0,
|
|
29658
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
29659
|
+
});
|
|
29660
|
+
},
|
|
29311
29661
|
placeholder: "Ask me about production optimization, quality metrics, or any manufacturing challenge...",
|
|
29312
29662
|
className: "w-full resize-none bg-transparent px-2 py-2 pr-12 focus:outline-none placeholder-gray-500 text-gray-900 text-sm leading-relaxed",
|
|
29313
29663
|
rows: 1,
|
|
@@ -29342,14 +29692,7 @@ var AIAgentView = () => {
|
|
|
29342
29692
|
{
|
|
29343
29693
|
className: `flex gap-4 ${message.role === "user" ? "justify-end" : "justify-start"}`,
|
|
29344
29694
|
children: [
|
|
29345
|
-
message.role === "assistant" && /* @__PURE__ */ jsx(
|
|
29346
|
-
"img",
|
|
29347
|
-
{
|
|
29348
|
-
src: axelProfilePng,
|
|
29349
|
-
alt: "Axel",
|
|
29350
|
-
className: "w-full h-full object-cover"
|
|
29351
|
-
}
|
|
29352
|
-
) }) }),
|
|
29695
|
+
message.role === "assistant" && /* @__PURE__ */ jsx(ProfilePicture, {}),
|
|
29353
29696
|
/* @__PURE__ */ jsx("div", { className: `max-w-none w-full group ${message.role === "user" ? "order-1" : ""}`, children: /* @__PURE__ */ jsx(
|
|
29354
29697
|
"div",
|
|
29355
29698
|
{
|
|
@@ -29362,17 +29705,10 @@ var AIAgentView = () => {
|
|
|
29362
29705
|
) })
|
|
29363
29706
|
]
|
|
29364
29707
|
},
|
|
29365
|
-
message.id === -1 ?
|
|
29708
|
+
message.id === -1 ? "streaming-message" : `${message.id}-${index}`
|
|
29366
29709
|
)),
|
|
29367
29710
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-4 justify-start", children: [
|
|
29368
|
-
/* @__PURE__ */ jsx(
|
|
29369
|
-
"img",
|
|
29370
|
-
{
|
|
29371
|
-
src: axelProfilePng,
|
|
29372
|
-
alt: "Axel",
|
|
29373
|
-
className: "w-full h-full object-cover"
|
|
29374
|
-
}
|
|
29375
|
-
) }) }),
|
|
29711
|
+
/* @__PURE__ */ jsx(ProfilePicture, {}),
|
|
29376
29712
|
/* @__PURE__ */ jsx("div", { className: "bg-white border border-gray-200/80 px-5 py-4 rounded-2xl shadow-sm max-w-full", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
29377
29713
|
/* @__PURE__ */ jsxs("div", { className: "flex space-x-1", children: [
|
|
29378
29714
|
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-bounce" }),
|
|
@@ -29391,14 +29727,7 @@ var AIAgentView = () => {
|
|
|
29391
29727
|
{
|
|
29392
29728
|
className: `flex gap-4 ${message.role === "user" ? "justify-end" : "justify-start"}`,
|
|
29393
29729
|
children: [
|
|
29394
|
-
message.role === "assistant" && /* @__PURE__ */ jsx(
|
|
29395
|
-
"img",
|
|
29396
|
-
{
|
|
29397
|
-
src: axelProfilePng,
|
|
29398
|
-
alt: "Axel",
|
|
29399
|
-
className: "w-full h-full object-cover"
|
|
29400
|
-
}
|
|
29401
|
-
) }) }),
|
|
29730
|
+
message.role === "assistant" && /* @__PURE__ */ jsx(ProfilePicture, {}),
|
|
29402
29731
|
/* @__PURE__ */ jsxs("div", { className: `max-w-none w-full group ${message.role === "user" ? "order-1" : ""}`, children: [
|
|
29403
29732
|
/* @__PURE__ */ jsxs(
|
|
29404
29733
|
"div",
|
|
@@ -29435,7 +29764,7 @@ var AIAgentView = () => {
|
|
|
29435
29764
|
] })
|
|
29436
29765
|
]
|
|
29437
29766
|
},
|
|
29438
|
-
message.id === -1 ?
|
|
29767
|
+
message.id === -1 ? "streaming-message" : `${message.id}-${index}`
|
|
29439
29768
|
)),
|
|
29440
29769
|
lastError && /* @__PURE__ */ jsxs("div", { className: "flex gap-4 justify-start", children: [
|
|
29441
29770
|
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx("div", { className: "w-12 h-12 rounded-xl bg-red-100 flex items-center justify-center", children: /* @__PURE__ */ jsx(AlertCircle, { className: "w-6 h-6 text-red-600" }) }) }),
|
|
@@ -29452,14 +29781,7 @@ var AIAgentView = () => {
|
|
|
29452
29781
|
] })
|
|
29453
29782
|
] }),
|
|
29454
29783
|
isCurrentThreadLoading && !currentStreaming.message && /* @__PURE__ */ jsxs("div", { className: "flex gap-4 justify-start", children: [
|
|
29455
|
-
/* @__PURE__ */ jsx(
|
|
29456
|
-
"img",
|
|
29457
|
-
{
|
|
29458
|
-
src: axelProfilePng,
|
|
29459
|
-
alt: "Axel",
|
|
29460
|
-
className: "w-full h-full object-cover"
|
|
29461
|
-
}
|
|
29462
|
-
) }) }),
|
|
29784
|
+
/* @__PURE__ */ jsx(ProfilePicture, {}),
|
|
29463
29785
|
/* @__PURE__ */ jsx("div", { className: "bg-white border border-gray-200/80 px-5 py-4 rounded-2xl shadow-sm max-w-full", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
29464
29786
|
/* @__PURE__ */ jsxs("div", { className: "flex space-x-1", children: [
|
|
29465
29787
|
/* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-bounce" }),
|
|
@@ -29500,8 +29822,30 @@ var AIAgentView = () => {
|
|
|
29500
29822
|
{
|
|
29501
29823
|
ref: textareaRef,
|
|
29502
29824
|
value: inputValue,
|
|
29503
|
-
onChange: (e) =>
|
|
29825
|
+
onChange: (e) => {
|
|
29826
|
+
const newValue = e.target.value;
|
|
29827
|
+
setInputValue(newValue);
|
|
29828
|
+
if (newValue.length > 0 && !hasStartedTyping) {
|
|
29829
|
+
trackTypingStart();
|
|
29830
|
+
}
|
|
29831
|
+
if (newValue.length > 0) {
|
|
29832
|
+
trackTypingProgress(newValue);
|
|
29833
|
+
}
|
|
29834
|
+
if (newValue.length === 0) {
|
|
29835
|
+
resetTypingState();
|
|
29836
|
+
}
|
|
29837
|
+
},
|
|
29504
29838
|
onKeyDown: handleKeyDown,
|
|
29839
|
+
onFocus: () => {
|
|
29840
|
+
trackCoreEvent("AI Agent Input Focused", {
|
|
29841
|
+
line_id: lineId,
|
|
29842
|
+
company_id: companyId,
|
|
29843
|
+
shift_id: shiftId,
|
|
29844
|
+
active_thread_id: activeThreadId,
|
|
29845
|
+
has_existing_messages: messages.length > 0,
|
|
29846
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
29847
|
+
});
|
|
29848
|
+
},
|
|
29505
29849
|
placeholder: "Ask me about production optimization, quality metrics, or any manufacturing challenge...",
|
|
29506
29850
|
className: "w-full resize-none bg-transparent px-2 py-2 pr-12 focus:outline-none placeholder-gray-500 text-gray-900 text-sm leading-relaxed",
|
|
29507
29851
|
rows: 1,
|
|
@@ -31415,7 +31759,8 @@ var parseBreaksFromDB = (dbBreaks) => {
|
|
|
31415
31759
|
duration: calculateBreakDuration(
|
|
31416
31760
|
breakItem.start || breakItem.startTime || "00:00",
|
|
31417
31761
|
breakItem.end || breakItem.endTime || "00:00"
|
|
31418
|
-
)
|
|
31762
|
+
),
|
|
31763
|
+
remarks: breakItem.remarks || breakItem.name || ""
|
|
31419
31764
|
}));
|
|
31420
31765
|
} else if (dbBreaks.breaks && Array.isArray(dbBreaks.breaks)) {
|
|
31421
31766
|
return dbBreaks.breaks.map((breakItem) => ({
|
|
@@ -31424,7 +31769,8 @@ var parseBreaksFromDB = (dbBreaks) => {
|
|
|
31424
31769
|
duration: calculateBreakDuration(
|
|
31425
31770
|
breakItem.start || breakItem.startTime || "00:00",
|
|
31426
31771
|
breakItem.end || breakItem.endTime || "00:00"
|
|
31427
|
-
)
|
|
31772
|
+
),
|
|
31773
|
+
remarks: breakItem.remarks || breakItem.name || ""
|
|
31428
31774
|
}));
|
|
31429
31775
|
} else {
|
|
31430
31776
|
console.warn("Unexpected breaks format:", dbBreaks);
|
|
@@ -31442,7 +31788,8 @@ var formatBreaks = (breaks) => {
|
|
|
31442
31788
|
return {
|
|
31443
31789
|
breaks: breaks.map((breakItem) => ({
|
|
31444
31790
|
start: breakItem.startTime,
|
|
31445
|
-
end: breakItem.endTime
|
|
31791
|
+
end: breakItem.endTime,
|
|
31792
|
+
remarks: breakItem.remarks || ""
|
|
31446
31793
|
}))
|
|
31447
31794
|
};
|
|
31448
31795
|
};
|
|
@@ -31452,13 +31799,14 @@ var BreakRow = memo(({
|
|
|
31452
31799
|
onRemove,
|
|
31453
31800
|
index
|
|
31454
31801
|
}) => {
|
|
31455
|
-
return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-
|
|
31802
|
+
return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-2 sm:gap-4 items-center w-full bg-white hover:bg-gray-50 rounded-md transition-all duration-200 p-2", children: [
|
|
31456
31803
|
/* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
|
|
31457
31804
|
"input",
|
|
31458
31805
|
{
|
|
31459
31806
|
type: "time",
|
|
31460
31807
|
value: breakItem.startTime,
|
|
31461
31808
|
onChange: (e) => onUpdate(index, "startTime", e.target.value),
|
|
31809
|
+
step: "60",
|
|
31462
31810
|
className: "w-full px-2 sm:px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
31463
31811
|
}
|
|
31464
31812
|
) }),
|
|
@@ -31468,6 +31816,7 @@ var BreakRow = memo(({
|
|
|
31468
31816
|
type: "time",
|
|
31469
31817
|
value: breakItem.endTime,
|
|
31470
31818
|
onChange: (e) => onUpdate(index, "endTime", e.target.value),
|
|
31819
|
+
step: "60",
|
|
31471
31820
|
className: "w-full px-2 sm:px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
31472
31821
|
}
|
|
31473
31822
|
) }),
|
|
@@ -31475,6 +31824,16 @@ var BreakRow = memo(({
|
|
|
31475
31824
|
breakItem.duration,
|
|
31476
31825
|
" min"
|
|
31477
31826
|
] }) }),
|
|
31827
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsx(
|
|
31828
|
+
"input",
|
|
31829
|
+
{
|
|
31830
|
+
type: "text",
|
|
31831
|
+
value: breakItem.remarks || "",
|
|
31832
|
+
onChange: (e) => onUpdate(index, "remarks", e.target.value),
|
|
31833
|
+
placeholder: "Break remarks",
|
|
31834
|
+
className: "w-full px-2 sm:px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
31835
|
+
}
|
|
31836
|
+
) }),
|
|
31478
31837
|
/* @__PURE__ */ jsx("div", { className: "col-span-1 flex justify-center", children: /* @__PURE__ */ jsx(
|
|
31479
31838
|
"button",
|
|
31480
31839
|
{
|
|
@@ -31591,10 +31950,11 @@ var ShiftPanel = memo(({
|
|
|
31591
31950
|
"Breaks"
|
|
31592
31951
|
] }) }),
|
|
31593
31952
|
/* @__PURE__ */ jsx("div", { className: "space-y-2 mb-4 w-full", children: breaks.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
31594
|
-
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-
|
|
31953
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 gap-2 sm:gap-4 text-xs font-medium text-gray-500 mb-1 w-full px-2", children: [
|
|
31595
31954
|
/* @__PURE__ */ jsx("div", { className: "col-span-3", children: "Break Start" }),
|
|
31596
31955
|
/* @__PURE__ */ jsx("div", { className: "col-span-3", children: "Break End" }),
|
|
31597
31956
|
/* @__PURE__ */ jsx("div", { className: "col-span-2", children: "Duration" }),
|
|
31957
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-3", children: "Remarks" }),
|
|
31598
31958
|
/* @__PURE__ */ jsx("div", { className: "col-span-1" })
|
|
31599
31959
|
] }),
|
|
31600
31960
|
/* @__PURE__ */ jsx("div", { className: "bg-gray-50/80 p-2 rounded-md", children: breaks.map((breakItem, index) => /* @__PURE__ */ jsx(
|
|
@@ -31807,7 +32167,8 @@ var ShiftsView = ({
|
|
|
31807
32167
|
const newBreak = {
|
|
31808
32168
|
startTime: dayShift.startTime,
|
|
31809
32169
|
endTime: dayShift.startTime,
|
|
31810
|
-
duration: 0
|
|
32170
|
+
duration: 0,
|
|
32171
|
+
remarks: ""
|
|
31811
32172
|
};
|
|
31812
32173
|
return {
|
|
31813
32174
|
...typedConfig,
|
|
@@ -31848,14 +32209,16 @@ var ShiftsView = ({
|
|
|
31848
32209
|
const dayShift = { ...typedConfig.dayShift };
|
|
31849
32210
|
const newBreaks = [...dayShift.breaks];
|
|
31850
32211
|
newBreaks[index] = { ...newBreaks[index], [field]: value };
|
|
31851
|
-
|
|
31852
|
-
|
|
31853
|
-
|
|
31854
|
-
|
|
31855
|
-
|
|
31856
|
-
endMinutes
|
|
31857
|
-
|
|
31858
|
-
|
|
32212
|
+
if (field === "startTime" || field === "endTime") {
|
|
32213
|
+
const startParts = newBreaks[index].startTime.split(":").map(Number);
|
|
32214
|
+
const endParts = newBreaks[index].endTime.split(":").map(Number);
|
|
32215
|
+
let startMinutes = startParts[0] * 60 + startParts[1];
|
|
32216
|
+
let endMinutes = endParts[0] * 60 + endParts[1];
|
|
32217
|
+
if (endMinutes < startMinutes) {
|
|
32218
|
+
endMinutes += 24 * 60;
|
|
32219
|
+
}
|
|
32220
|
+
newBreaks[index].duration = endMinutes - startMinutes;
|
|
32221
|
+
}
|
|
31859
32222
|
return {
|
|
31860
32223
|
...typedConfig,
|
|
31861
32224
|
dayShift: {
|
|
@@ -31874,14 +32237,16 @@ var ShiftsView = ({
|
|
|
31874
32237
|
const nightShift = { ...typedConfig.nightShift };
|
|
31875
32238
|
const newBreaks = [...nightShift.breaks];
|
|
31876
32239
|
newBreaks[index] = { ...newBreaks[index], [field]: value };
|
|
31877
|
-
|
|
31878
|
-
|
|
31879
|
-
|
|
31880
|
-
|
|
31881
|
-
|
|
31882
|
-
endMinutes
|
|
31883
|
-
|
|
31884
|
-
|
|
32240
|
+
if (field === "startTime" || field === "endTime") {
|
|
32241
|
+
const startParts = newBreaks[index].startTime.split(":").map(Number);
|
|
32242
|
+
const endParts = newBreaks[index].endTime.split(":").map(Number);
|
|
32243
|
+
let startMinutes = startParts[0] * 60 + startParts[1];
|
|
32244
|
+
let endMinutes = endParts[0] * 60 + endParts[1];
|
|
32245
|
+
if (endMinutes < startMinutes) {
|
|
32246
|
+
endMinutes += 24 * 60;
|
|
32247
|
+
}
|
|
32248
|
+
newBreaks[index].duration = endMinutes - startMinutes;
|
|
32249
|
+
}
|
|
31885
32250
|
return {
|
|
31886
32251
|
...typedConfig,
|
|
31887
32252
|
nightShift: {
|