@optifye/dashboard-core 4.1.0 → 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.js CHANGED
@@ -25337,6 +25337,32 @@ var S3ClipsService = class {
25337
25337
  return null;
25338
25338
  }
25339
25339
  }
25340
+ /**
25341
+ * Fetches full metadata including timestamps
25342
+ */
25343
+ async getFullMetadata(playlistUri) {
25344
+ try {
25345
+ const metadataUri = playlistUri.replace(/playlist\.m3u8$/, "metadata.json");
25346
+ const url = new URL(metadataUri);
25347
+ const bucket = url.hostname;
25348
+ const key = url.pathname.substring(1);
25349
+ const command = new clientS3.GetObjectCommand({
25350
+ Bucket: bucket,
25351
+ Key: key
25352
+ });
25353
+ const response = await this.s3Client.send(command);
25354
+ if (!response.Body) {
25355
+ console.warn(`Empty response body for metadata file: ${key}`);
25356
+ return null;
25357
+ }
25358
+ const metadataContent = await response.Body.transformToString();
25359
+ const metadata = JSON.parse(metadataContent);
25360
+ return metadata;
25361
+ } catch (error) {
25362
+ console.error(`Error fetching or parsing metadata:`, error);
25363
+ return null;
25364
+ }
25365
+ }
25340
25366
  /**
25341
25367
  * Converts S3 URI to CloudFront URL
25342
25368
  * Uses streaming proxy for localhost development to handle CORS
@@ -25349,15 +25375,22 @@ var S3ClipsService = class {
25349
25375
  /**
25350
25376
  * Processes a single video completely
25351
25377
  */
25352
- async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime) {
25378
+ async processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime, includeMetadata = false) {
25353
25379
  const parsedInfo = parseS3Uri(uri);
25354
25380
  if (!parsedInfo) {
25355
25381
  console.warn(`Skipping URI due to parsing failure: ${uri}`);
25356
25382
  return null;
25357
25383
  }
25358
25384
  let cycleTimeSeconds = null;
25359
- if (includeCycleTime && (parsedInfo.type === "bottleneck" && parsedInfo.description.toLowerCase().includes("cycle time") || parsedInfo.type === "best_cycle_time" || parsedInfo.type === "worst_cycle_time")) {
25360
- cycleTimeSeconds = await this.getMetadataCycleTime(uri);
25385
+ let creationTimestamp = void 0;
25386
+ if (includeMetadata || includeCycleTime && (parsedInfo.type === "bottleneck" && parsedInfo.description.toLowerCase().includes("cycle time") || parsedInfo.type === "best_cycle_time" || parsedInfo.type === "worst_cycle_time")) {
25387
+ const metadata = await this.getFullMetadata(uri);
25388
+ if (metadata) {
25389
+ if (metadata.original_task_metadata?.cycle_time) {
25390
+ cycleTimeSeconds = metadata.original_task_metadata.cycle_time;
25391
+ }
25392
+ creationTimestamp = metadata.upload_timestamp || metadata.original_task_metadata?.timestamp || metadata.creation_timestamp || metadata[""];
25393
+ }
25361
25394
  }
25362
25395
  const cloudfrontPlaylistUrl = this.s3UriToCloudfront(uri);
25363
25396
  const { type: videoType, timestamp: videoTimestamp } = parsedInfo;
@@ -25366,7 +25399,8 @@ var S3ClipsService = class {
25366
25399
  src: cloudfrontPlaylistUrl,
25367
25400
  // Direct CloudFront playlist URL
25368
25401
  ...parsedInfo,
25369
- cycle_time_seconds: cycleTimeSeconds || void 0
25402
+ cycle_time_seconds: cycleTimeSeconds || void 0,
25403
+ creation_timestamp: creationTimestamp
25370
25404
  };
25371
25405
  }
25372
25406
  /**
@@ -25428,7 +25462,7 @@ var S3ClipsService = class {
25428
25462
  * Main method to fetch clips based on parameters
25429
25463
  */
25430
25464
  async fetchClips(params) {
25431
- const { workspaceId, date: inputDate, shift, category, limit, mode, includeCycleTime } = params;
25465
+ const { workspaceId, date: inputDate, shift, category, limit, mode, includeCycleTime, includeMetadata, timestampStart, timestampEnd } = params;
25432
25466
  if (!workspaceId) {
25433
25467
  throw new Error("Valid Workspace ID is required");
25434
25468
  }
@@ -25533,7 +25567,7 @@ var S3ClipsService = class {
25533
25567
  const batch = filteredUris.slice(i, i + concurrencyLimit);
25534
25568
  const batchPromises = batch.map(async (uri, batchIndex) => {
25535
25569
  const index = i + batchIndex;
25536
- const result = await this.processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime || false);
25570
+ const result = await this.processFullVideo(uri, index, workspaceId, date, shiftId, includeCycleTime || false, includeMetadata || (!!timestampStart || !!timestampEnd));
25537
25571
  processedCount++;
25538
25572
  if (processedCount % 10 === 0) {
25539
25573
  console.log(`S3ClipsService: Processed ${processedCount}/${filteredUris.length} videos for category '${category || "all"}'...`);
@@ -25543,7 +25577,26 @@ var S3ClipsService = class {
25543
25577
  const batchResults = await Promise.all(batchPromises);
25544
25578
  videoResults.push(...batchResults);
25545
25579
  }
25546
- const videos = videoResults.filter((v) => v !== null);
25580
+ let videos = videoResults.filter((v) => v !== null);
25581
+ if (timestampStart || timestampEnd) {
25582
+ videos = videos.filter((video) => {
25583
+ if (!video.creation_timestamp) return false;
25584
+ const videoTimestamp = new Date(video.creation_timestamp).getTime();
25585
+ if (timestampStart && timestampEnd) {
25586
+ const start = new Date(timestampStart).getTime();
25587
+ const end = new Date(timestampEnd).getTime();
25588
+ return videoTimestamp >= start && videoTimestamp <= end;
25589
+ } else if (timestampStart) {
25590
+ const start = new Date(timestampStart).getTime();
25591
+ return videoTimestamp >= start;
25592
+ } else if (timestampEnd) {
25593
+ const end = new Date(timestampEnd).getTime();
25594
+ return videoTimestamp <= end;
25595
+ }
25596
+ return true;
25597
+ });
25598
+ console.log(`S3ClipsService: Filtered by timestamp - ${videos.length} videos remain after filtering`);
25599
+ }
25547
25600
  console.log(`S3ClipsService: Successfully processed ${videos.length} out of ${filteredUris.length} video clips for category '${category || "all"}' (limit: ${limitPerCategory} per category).`);
25548
25601
  const typeCounts = videos.reduce((acc, video) => {
25549
25602
  acc[video.type] = (acc[video.type] || 0) + 1;
@@ -25588,6 +25641,7 @@ var BottlenecksContent = ({
25588
25641
  const dashboardConfig = useDashboardConfig();
25589
25642
  const videoRef = React46.useRef(null);
25590
25643
  const fullscreenContainerRef = React46.useRef(null);
25644
+ const timestampFilterRef = React46.useRef(null);
25591
25645
  const [isPlaying, setIsPlaying] = React46.useState(false);
25592
25646
  const [currentTime, setCurrentTime] = React46.useState(0);
25593
25647
  const [duration, setDuration] = React46.useState(0);
@@ -25597,6 +25651,22 @@ var BottlenecksContent = ({
25597
25651
  const [allVideos, setAllVideos] = React46.useState([]);
25598
25652
  const [isLoading, setIsLoading] = React46.useState(true);
25599
25653
  const [error, setError] = React46.useState(null);
25654
+ const [showTimestampFilter, setShowTimestampFilter] = React46.useState(false);
25655
+ const [timestampStart, setTimestampStart] = React46.useState("");
25656
+ const [timestampEnd, setTimestampEnd] = React46.useState("");
25657
+ React46.useEffect(() => {
25658
+ const handleClickOutside = (event) => {
25659
+ if (timestampFilterRef.current && !timestampFilterRef.current.contains(event.target)) {
25660
+ setShowTimestampFilter(false);
25661
+ }
25662
+ };
25663
+ if (showTimestampFilter) {
25664
+ document.addEventListener("mousedown", handleClickOutside);
25665
+ }
25666
+ return () => {
25667
+ document.removeEventListener("mousedown", handleClickOutside);
25668
+ };
25669
+ }, [showTimestampFilter]);
25600
25670
  const s3ClipsService = React46.useMemo(() => {
25601
25671
  if (!dashboardConfig?.s3Config) {
25602
25672
  console.warn("S3 configuration not found in dashboard config");
@@ -25609,13 +25679,26 @@ var BottlenecksContent = ({
25609
25679
  setIsLoading(true);
25610
25680
  setError(null);
25611
25681
  try {
25682
+ const operationalDate = date || getOperationalDate();
25683
+ let timestampStartFull;
25684
+ let timestampEndFull;
25685
+ if (timestampStart) {
25686
+ timestampStartFull = `${operationalDate}T${timestampStart}:00`;
25687
+ }
25688
+ if (timestampEnd) {
25689
+ timestampEndFull = `${operationalDate}T${timestampEnd}:00`;
25690
+ }
25612
25691
  const videos = await s3ClipsService.fetchClips({
25613
25692
  workspaceId,
25614
- date: date || getOperationalDate(),
25693
+ date: operationalDate,
25615
25694
  mode: "full",
25616
25695
  includeCycleTime: true,
25617
- limit: 50
25696
+ includeMetadata: true,
25697
+ // Always include metadata for timestamp info
25698
+ limit: 50,
25618
25699
  // Reasonable limit for UI performance
25700
+ timestampStart: timestampStartFull,
25701
+ timestampEnd: timestampEndFull
25619
25702
  });
25620
25703
  if (Array.isArray(videos) && videos.length > 0) {
25621
25704
  preloadVideoUrl2(videos[0].src);
@@ -25643,7 +25726,7 @@ var BottlenecksContent = ({
25643
25726
  } finally {
25644
25727
  setIsLoading(false);
25645
25728
  }
25646
- }, [workspaceId, date, s3ClipsService]);
25729
+ }, [workspaceId, date, s3ClipsService, timestampStart, timestampEnd]);
25647
25730
  React46.useEffect(() => {
25648
25731
  if (s3ClipsService) {
25649
25732
  fetchClips();
@@ -26012,6 +26095,43 @@ var BottlenecksContent = ({
26012
26095
  return "Bottleneck";
26013
26096
  }
26014
26097
  };
26098
+ const formatTimestamp = (timestamp) => {
26099
+ if (!timestamp) return "";
26100
+ try {
26101
+ const date2 = new Date(timestamp);
26102
+ const today = /* @__PURE__ */ new Date();
26103
+ const isToday = date2.toDateString() === today.toDateString();
26104
+ if (isToday) {
26105
+ return date2.toLocaleString("en-US", {
26106
+ hour: "numeric",
26107
+ minute: "2-digit",
26108
+ hour12: true
26109
+ });
26110
+ } else {
26111
+ return date2.toLocaleString("en-US", {
26112
+ month: "short",
26113
+ day: "numeric",
26114
+ hour: "numeric",
26115
+ minute: "2-digit",
26116
+ hour12: true
26117
+ });
26118
+ }
26119
+ } catch {
26120
+ return "";
26121
+ }
26122
+ };
26123
+ const formatTimeOnly = (time2) => {
26124
+ if (!time2) return "";
26125
+ try {
26126
+ const [hours, minutes] = time2.split(":");
26127
+ const hour = parseInt(hours);
26128
+ const ampm = hour >= 12 ? "PM" : "AM";
26129
+ const displayHour = hour % 12 || 12;
26130
+ return `${displayHour}:${minutes} ${ampm}`;
26131
+ } catch {
26132
+ return time2;
26133
+ }
26134
+ };
26015
26135
  if (!dashboardConfig?.s3Config) {
26016
26136
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-grow p-4 flex flex-col items-center justify-center h-[calc(100vh-12rem)] text-center", children: [
26017
26137
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XCircle, { className: "w-12 h-12 text-red-400 mb-3" }),
@@ -26120,6 +26240,74 @@ var BottlenecksContent = ({
26120
26240
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 border-b border-gray-100", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center", children: [
26121
26241
  /* @__PURE__ */ jsxRuntime.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})` }),
26122
26242
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2", children: [
26243
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", ref: timestampFilterRef, children: [
26244
+ /* @__PURE__ */ jsxRuntime.jsx(
26245
+ "button",
26246
+ {
26247
+ onClick: () => setShowTimestampFilter(!showTimestampFilter),
26248
+ 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"}`,
26249
+ "aria-label": "Filter by time",
26250
+ title: "Filter by time",
26251
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-5 w-5" })
26252
+ }
26253
+ ),
26254
+ showTimestampFilter && /* @__PURE__ */ jsxRuntime.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: [
26255
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold text-gray-700 mb-3", children: "Filter by Time" }),
26256
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
26257
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
26258
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "timestamp-start", className: "block text-xs font-medium text-gray-600 mb-1", children: "Start Time" }),
26259
+ /* @__PURE__ */ jsxRuntime.jsx(
26260
+ "input",
26261
+ {
26262
+ id: "timestamp-start",
26263
+ type: "time",
26264
+ value: timestampStart,
26265
+ onChange: (e) => setTimestampStart(e.target.value),
26266
+ 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"
26267
+ }
26268
+ )
26269
+ ] }),
26270
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
26271
+ /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: "timestamp-end", className: "block text-xs font-medium text-gray-600 mb-1", children: "End Time" }),
26272
+ /* @__PURE__ */ jsxRuntime.jsx(
26273
+ "input",
26274
+ {
26275
+ id: "timestamp-end",
26276
+ type: "time",
26277
+ value: timestampEnd,
26278
+ onChange: (e) => setTimestampEnd(e.target.value),
26279
+ 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"
26280
+ }
26281
+ )
26282
+ ] }),
26283
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between pt-2", children: [
26284
+ /* @__PURE__ */ jsxRuntime.jsx(
26285
+ "button",
26286
+ {
26287
+ onClick: () => {
26288
+ setTimestampStart("");
26289
+ setTimestampEnd("");
26290
+ setShowTimestampFilter(false);
26291
+ },
26292
+ className: "px-3 py-1.5 text-sm text-gray-600 hover:text-gray-800 transition-colors",
26293
+ children: "Clear"
26294
+ }
26295
+ ),
26296
+ /* @__PURE__ */ jsxRuntime.jsx(
26297
+ "button",
26298
+ {
26299
+ onClick: () => {
26300
+ setShowTimestampFilter(false);
26301
+ fetchClips();
26302
+ },
26303
+ className: "px-3 py-1.5 text-sm bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors",
26304
+ children: "Apply Filter"
26305
+ }
26306
+ )
26307
+ ] })
26308
+ ] })
26309
+ ] })
26310
+ ] }),
26123
26311
  /* @__PURE__ */ jsxRuntime.jsx(
26124
26312
  "button",
26125
26313
  {
@@ -26143,6 +26331,29 @@ var BottlenecksContent = ({
26143
26331
  )
26144
26332
  ] })
26145
26333
  ] }) }),
26334
+ (timestampStart || timestampEnd) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-2 bg-blue-50 border-b border-blue-100 flex items-center justify-between", children: [
26335
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center space-x-2 text-sm text-blue-700", children: [
26336
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-4 w-4" }),
26337
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
26338
+ "Filtered by time: ",
26339
+ timestampStart ? formatTimeOnly(timestampStart) : "Any",
26340
+ " - ",
26341
+ timestampEnd ? formatTimeOnly(timestampEnd) : "Any"
26342
+ ] })
26343
+ ] }),
26344
+ /* @__PURE__ */ jsxRuntime.jsx(
26345
+ "button",
26346
+ {
26347
+ onClick: () => {
26348
+ setTimestampStart("");
26349
+ setTimestampEnd("");
26350
+ fetchClips();
26351
+ },
26352
+ className: "text-sm text-blue-600 hover:text-blue-800 transition-colors",
26353
+ children: "Clear filter"
26354
+ }
26355
+ )
26356
+ ] }),
26146
26357
  isLoading && allVideos.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner2, { size: "md", message: "Loading clips..." }) }) : allVideos.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-[calc(100%-4rem)]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center p-8", children: [
26147
26358
  /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.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" }) }),
26148
26359
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-medium text-gray-700 mb-2", children: "No Clips Found" }),
@@ -26202,6 +26413,7 @@ var BottlenecksContent = ({
26202
26413
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-80 hidden sm:inline", children: currentVideo.description })
26203
26414
  ] }) })
26204
26415
  ),
26416
+ currentVideo.creation_timestamp && /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx("span", { className: "opacity-80", children: formatTimestamp(currentVideo.creation_timestamp) }) }),
26205
26417
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-0 left-0 right-0 p-3 bg-gradient-to-t from-black/70 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-white", children: [
26206
26418
  /* @__PURE__ */ jsxRuntime.jsx(
26207
26419
  "button",
@@ -28122,9 +28334,9 @@ var SideNavBar = React46.memo(({
28122
28334
  }
28123
28335
  )
28124
28336
  ] }),
28125
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto", children: [
28126
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-px bg-gray-200 my-4" }),
28127
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pb-2 w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
28337
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto mb-2", children: [
28338
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-px bg-gray-200 my-3" }),
28339
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
28128
28340
  "button",
28129
28341
  {
28130
28342
  onClick: handleProfileClick,
@@ -28706,7 +28918,7 @@ var ThreadSidebar = ({
28706
28918
  if (error) {
28707
28919
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `p-4 text-red-600 text-sm ${className}`, children: "Failed to load conversations" });
28708
28920
  }
28709
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col h-full bg-gray-50 border-r border-gray-200 ${className}`, children: [
28921
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex flex-col h-screen bg-gray-50 border-r border-gray-200 ${className}`, children: [
28710
28922
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0 p-4 border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs(
28711
28923
  "button",
28712
28924
  {
@@ -28718,7 +28930,7 @@ var ThreadSidebar = ({
28718
28930
  ]
28719
28931
  }
28720
28932
  ) }),
28721
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center p-8", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner_default, { size: "sm" }) }) : threads.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center text-gray-500 text-sm", children: "No conversations yet" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-2", children: threads.map((thread) => /* @__PURE__ */ jsxRuntime.jsxs(
28933
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto min-h-0", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center p-8", children: /* @__PURE__ */ jsxRuntime.jsx(LoadingSpinner_default, { size: "sm" }) }) : threads.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4 text-center text-gray-500 text-sm", children: "No conversations yet" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-2", children: threads.map((thread) => /* @__PURE__ */ jsxRuntime.jsxs(
28722
28934
  "div",
28723
28935
  {
28724
28936
  onClick: () => onSelectThread(thread.id),
@@ -28750,6 +28962,23 @@ var ThreadSidebar = ({
28750
28962
  ] });
28751
28963
  };
28752
28964
  var axelProfilePng = "/axel-profile.png";
28965
+ var ProfilePicture = React46__namespace.default.memo(({ alt = "Axel", className = "w-12 h-12" }) => {
28966
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${className} rounded-xl overflow-hidden shadow-sm`, children: /* @__PURE__ */ jsxRuntime.jsx(
28967
+ "img",
28968
+ {
28969
+ src: axelProfilePng,
28970
+ alt,
28971
+ className: "w-full h-full object-cover",
28972
+ loading: "eager",
28973
+ decoding: "async"
28974
+ }
28975
+ ) }) });
28976
+ });
28977
+ ProfilePicture.displayName = "ProfilePicture";
28978
+ var preloadImage = (src) => {
28979
+ const img = new Image();
28980
+ img.src = src;
28981
+ };
28753
28982
  var AIAgentView = () => {
28754
28983
  const { navigate, pathname } = useNavigation();
28755
28984
  const config = useDashboardConfig();
@@ -28761,16 +28990,92 @@ var AIAgentView = () => {
28761
28990
  const [lastError, setLastError] = React46.useState(null);
28762
28991
  const [copiedMessageId, setCopiedMessageId] = React46.useState(null);
28763
28992
  const [activeThreadId, setActiveThreadId] = React46.useState(void 0);
28764
- const [isSidebarOpen, setIsSidebarOpen] = React46.useState(true);
28993
+ const [isSidebarOpen, setIsSidebarOpen] = React46.useState(false);
28765
28994
  const [streamingStates, setStreamingStates] = React46.useState(/* @__PURE__ */ new Map());
28766
28995
  const [userId, setUserId] = React46.useState(null);
28767
28996
  const [pendingThreadId, setPendingThreadId] = React46.useState(null);
28997
+ const [isTransitioning, setIsTransitioning] = React46.useState(false);
28998
+ const [typedText, setTypedText] = React46.useState("");
28999
+ const [newChatCount, setNewChatCount] = React46.useState(0);
29000
+ const [hasStartedTyping, setHasStartedTyping] = React46.useState(false);
29001
+ const [typingStartTime, setTypingStartTime] = React46.useState(null);
29002
+ const [lastTypingTime, setLastTypingTime] = React46.useState(null);
29003
+ const [characterCount, setCharacterCount] = React46.useState(0);
29004
+ const typingTimeoutRef = React46.useRef(null);
28768
29005
  const isThreadLoading = (threadId) => {
28769
29006
  return threadId ? loadingThreads.has(threadId) : false;
28770
29007
  };
28771
29008
  const getStreamingState = (threadId) => {
28772
29009
  return threadId ? streamingStates.get(threadId) || { message: "", reasoning: "" } : { message: "", reasoning: "" };
28773
29010
  };
29011
+ const trackTypingStart = () => {
29012
+ if (!hasStartedTyping) {
29013
+ const now2 = Date.now();
29014
+ setHasStartedTyping(true);
29015
+ setTypingStartTime(now2);
29016
+ setLastTypingTime(now2);
29017
+ trackCoreEvent("AI Agent Input Started", {
29018
+ line_id: lineId,
29019
+ company_id: companyId,
29020
+ shift_id: shiftId,
29021
+ active_thread_id: activeThreadId,
29022
+ has_existing_messages: messages.length > 0,
29023
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
29024
+ });
29025
+ }
29026
+ };
29027
+ const trackTypingProgress = (newValue) => {
29028
+ const now2 = Date.now();
29029
+ setLastTypingTime(now2);
29030
+ setCharacterCount(newValue.length);
29031
+ if (typingTimeoutRef.current) {
29032
+ clearTimeout(typingTimeoutRef.current);
29033
+ }
29034
+ typingTimeoutRef.current = setTimeout(() => {
29035
+ if (hasStartedTyping && typingStartTime && newValue.length > 0) {
29036
+ const typingDuration = now2 - typingStartTime;
29037
+ trackCoreEvent("AI Agent Input Typing Progress", {
29038
+ line_id: lineId,
29039
+ company_id: companyId,
29040
+ shift_id: shiftId,
29041
+ active_thread_id: activeThreadId,
29042
+ character_count: newValue.length,
29043
+ typing_duration_ms: typingDuration,
29044
+ has_existing_messages: messages.length > 0,
29045
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
29046
+ });
29047
+ }
29048
+ }, 2e3);
29049
+ };
29050
+ const trackMessageSent = (messageContent) => {
29051
+ if (hasStartedTyping && typingStartTime) {
29052
+ const now2 = Date.now();
29053
+ const totalTypingDuration = now2 - typingStartTime;
29054
+ trackCoreEvent("AI Agent Message Sent", {
29055
+ line_id: lineId,
29056
+ company_id: companyId,
29057
+ shift_id: shiftId,
29058
+ active_thread_id: activeThreadId,
29059
+ message_length: messageContent.length,
29060
+ character_count: messageContent.length,
29061
+ typing_duration_ms: totalTypingDuration,
29062
+ has_existing_messages: messages.length > 0,
29063
+ is_new_conversation: !activeThreadId,
29064
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
29065
+ });
29066
+ }
29067
+ resetTypingState();
29068
+ };
29069
+ const resetTypingState = () => {
29070
+ setHasStartedTyping(false);
29071
+ setTypingStartTime(null);
29072
+ setLastTypingTime(null);
29073
+ setCharacterCount(0);
29074
+ if (typingTimeoutRef.current) {
29075
+ clearTimeout(typingTimeoutRef.current);
29076
+ typingTimeoutRef.current = null;
29077
+ }
29078
+ };
28774
29079
  const textareaRef = React46.useRef(null);
28775
29080
  const messagesEndRef = React46.useRef(null);
28776
29081
  const containerRef = React46.useRef(null);
@@ -28792,6 +29097,41 @@ var AIAgentView = () => {
28792
29097
  const lineId = getLineIdFromPath();
28793
29098
  const { shiftId } = getCurrentShift(dateTimeConfig.defaultTimezone || "Asia/Kolkata", shiftConfig);
28794
29099
  const companyId = entityConfig.companyId || "default-company-id";
29100
+ const ACTIVE_THREAD_STORAGE_KEY = `ai-agent-active-thread-${lineId}`;
29101
+ React46.useLayoutEffect(() => {
29102
+ const savedThreadId = localStorage.getItem(ACTIVE_THREAD_STORAGE_KEY);
29103
+ if (savedThreadId && savedThreadId !== "undefined") {
29104
+ setActiveThreadId(savedThreadId);
29105
+ }
29106
+ }, [ACTIVE_THREAD_STORAGE_KEY]);
29107
+ React46.useEffect(() => {
29108
+ if (activeThreadId) {
29109
+ localStorage.setItem(ACTIVE_THREAD_STORAGE_KEY, activeThreadId);
29110
+ } else {
29111
+ localStorage.removeItem(ACTIVE_THREAD_STORAGE_KEY);
29112
+ }
29113
+ }, [activeThreadId, ACTIVE_THREAD_STORAGE_KEY]);
29114
+ React46.useEffect(() => {
29115
+ const handleVisibilityChange = () => {
29116
+ if (document.visibilityState === "hidden" && activeThreadId) {
29117
+ localStorage.setItem(ACTIVE_THREAD_STORAGE_KEY, activeThreadId);
29118
+ }
29119
+ };
29120
+ const handleBeforeUnload = () => {
29121
+ if (activeThreadId) {
29122
+ localStorage.setItem(ACTIVE_THREAD_STORAGE_KEY, activeThreadId);
29123
+ }
29124
+ };
29125
+ document.addEventListener("visibilitychange", handleVisibilityChange);
29126
+ window.addEventListener("beforeunload", handleBeforeUnload);
29127
+ return () => {
29128
+ document.removeEventListener("visibilitychange", handleVisibilityChange);
29129
+ window.removeEventListener("beforeunload", handleBeforeUnload);
29130
+ if (activeThreadId) {
29131
+ localStorage.setItem(ACTIVE_THREAD_STORAGE_KEY, activeThreadId);
29132
+ }
29133
+ };
29134
+ }, [activeThreadId, ACTIVE_THREAD_STORAGE_KEY]);
28795
29135
  React46.useEffect(() => {
28796
29136
  if (textareaRef.current) {
28797
29137
  textareaRef.current.style.height = "auto";
@@ -28806,6 +29146,27 @@ var AIAgentView = () => {
28806
29146
  setTimeout(scrollToBottom, 100);
28807
29147
  }
28808
29148
  }, [activeThreadId]);
29149
+ React46.useEffect(() => {
29150
+ if (messages.length === 0 && !isTransitioning) {
29151
+ const fullText = "Hi, I'm Axel - Your AI Supervisor";
29152
+ let index = 0;
29153
+ setTypedText("");
29154
+ const typeInterval = setInterval(() => {
29155
+ if (index < fullText.length) {
29156
+ setTypedText(fullText.substring(0, index + 1));
29157
+ index++;
29158
+ } else {
29159
+ clearInterval(typeInterval);
29160
+ }
29161
+ }, 50);
29162
+ return () => clearInterval(typeInterval);
29163
+ }
29164
+ }, [messages.length, isTransitioning]);
29165
+ React46.useEffect(() => {
29166
+ if (isSidebarOpen) {
29167
+ setNewChatCount(0);
29168
+ }
29169
+ }, [isSidebarOpen]);
28809
29170
  const copyToClipboard = async (text, messageId) => {
28810
29171
  try {
28811
29172
  await navigator.clipboard.writeText(text);
@@ -28820,8 +29181,21 @@ var AIAgentView = () => {
28820
29181
  setMessages([]);
28821
29182
  setInputValue("");
28822
29183
  setPendingThreadId(null);
29184
+ setTypedText("");
29185
+ resetTypingState();
29186
+ localStorage.removeItem(ACTIVE_THREAD_STORAGE_KEY);
28823
29187
  textareaRef.current?.focus();
28824
29188
  };
29189
+ React46.useEffect(() => {
29190
+ preloadImage(axelProfilePng);
29191
+ }, []);
29192
+ React46.useEffect(() => {
29193
+ return () => {
29194
+ if (typingTimeoutRef.current) {
29195
+ clearTimeout(typingTimeoutRef.current);
29196
+ }
29197
+ };
29198
+ }, []);
28825
29199
  React46.useEffect(() => {
28826
29200
  const checkAuth = async () => {
28827
29201
  const supabase2 = _getSupabaseInstance();
@@ -28852,6 +29226,13 @@ var AIAgentView = () => {
28852
29226
  let currentThreadId = activeThreadId || `temp-${Date.now()}`;
28853
29227
  if (isThreadLoading(currentThreadId)) return;
28854
29228
  const userMessage = inputValue.trim();
29229
+ trackMessageSent(userMessage);
29230
+ if (displayMessages.length === 0) {
29231
+ setIsTransitioning(true);
29232
+ setTimeout(() => {
29233
+ setIsTransitioning(false);
29234
+ }, 800);
29235
+ }
28855
29236
  setInputValue("");
28856
29237
  setLoadingThreads((prev) => new Set(prev).add(currentThreadId));
28857
29238
  setLastError(null);
@@ -28895,6 +29276,9 @@ var AIAgentView = () => {
28895
29276
  currentThreadId = threadId;
28896
29277
  setActiveThreadId(threadId);
28897
29278
  setPendingThreadId(null);
29279
+ if (!isSidebarOpen) {
29280
+ setNewChatCount((prev) => prev + 1);
29281
+ }
28898
29282
  setLoadingThreads((prev) => {
28899
29283
  const newSet = new Set(prev);
28900
29284
  if (newSet.has(oldThreadId)) {
@@ -28993,7 +29377,9 @@ var AIAgentView = () => {
28993
29377
  const handleKeyDown = (e) => {
28994
29378
  if (e.key === "Enter" && !e.shiftKey) {
28995
29379
  e.preventDefault();
28996
- handleSubmit(e);
29380
+ if (!isCurrentThreadLoading) {
29381
+ handleSubmit(e);
29382
+ }
28997
29383
  }
28998
29384
  };
28999
29385
  const formatMessage = (content) => {
@@ -29002,7 +29388,81 @@ var AIAgentView = () => {
29002
29388
  text = text.replace(/`([^`]+)`/g, '<code class="bg-gray-100 px-1.5 py-0.5 rounded text-sm font-mono text-gray-800">$1</code>');
29003
29389
  return text;
29004
29390
  };
29005
- const lines = content.split("\n");
29391
+ const parseTableFromText = (lines2, startIndex) => {
29392
+ const tableLines = [];
29393
+ let i = startIndex;
29394
+ while (i < lines2.length) {
29395
+ const line = lines2[i].trim();
29396
+ if (!line) {
29397
+ i++;
29398
+ break;
29399
+ }
29400
+ if (line.includes("|") || line.match(/^[-|=\s]+$/)) {
29401
+ tableLines.push(line);
29402
+ i++;
29403
+ } else {
29404
+ break;
29405
+ }
29406
+ }
29407
+ if (tableLines.length === 0) return { html: "", endIndex: startIndex };
29408
+ const dataLines = tableLines.filter((line) => !line.match(/^[-|=\s]+$/));
29409
+ if (dataLines.length === 0) return { html: "", endIndex: i };
29410
+ let headerRow = [];
29411
+ let dataRows = [];
29412
+ const firstLine = dataLines[0];
29413
+ if (firstLine.includes("|")) {
29414
+ const cells = firstLine.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
29415
+ if (cells.length >= 2) {
29416
+ headerRow = cells;
29417
+ for (let j = 1; j < dataLines.length; j++) {
29418
+ const row = dataLines[j];
29419
+ if (row.includes("|")) {
29420
+ const rowCells = row.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
29421
+ while (rowCells.length < headerRow.length) rowCells.push("");
29422
+ if (rowCells.length > headerRow.length) rowCells.splice(headerRow.length);
29423
+ dataRows.push(rowCells);
29424
+ }
29425
+ }
29426
+ }
29427
+ }
29428
+ if (headerRow.length === 0) {
29429
+ for (const line of dataLines) {
29430
+ if (line.includes("|")) {
29431
+ const rowCells = line.split("|").map((cell) => cell.trim()).filter((cell) => cell.length > 0);
29432
+ if (rowCells.length >= 2) {
29433
+ dataRows.push(rowCells);
29434
+ }
29435
+ }
29436
+ }
29437
+ if (dataRows.length > 0 && dataRows[0].length > 0) {
29438
+ headerRow = dataRows[0].map((_, index) => `Column ${index + 1}`);
29439
+ }
29440
+ }
29441
+ if (headerRow.length > 0 && dataRows.length > 0) {
29442
+ let tableHtml = '<div class="overflow-x-auto my-4"><table class="min-w-full border-collapse border border-gray-300 rounded-lg shadow-sm">';
29443
+ tableHtml += '<thead class="bg-gray-50">';
29444
+ tableHtml += "<tr>";
29445
+ headerRow.forEach((header) => {
29446
+ tableHtml += `<th class="border border-gray-300 px-4 py-2 text-left font-semibold text-gray-900">${processInlineFormatting(header)}</th>`;
29447
+ });
29448
+ tableHtml += "</tr>";
29449
+ tableHtml += "</thead>";
29450
+ tableHtml += '<tbody class="bg-white">';
29451
+ dataRows.forEach((row, rowIndex) => {
29452
+ tableHtml += `<tr class="${rowIndex % 2 === 0 ? "bg-white" : "bg-gray-50"}">`;
29453
+ row.forEach((cell) => {
29454
+ tableHtml += `<td class="border border-gray-300 px-4 py-2 text-gray-800">${processInlineFormatting(cell)}</td>`;
29455
+ });
29456
+ tableHtml += "</tr>";
29457
+ });
29458
+ tableHtml += "</tbody>";
29459
+ tableHtml += "</table></div>";
29460
+ return { html: tableHtml, endIndex: i };
29461
+ }
29462
+ return { html: "", endIndex: startIndex };
29463
+ };
29464
+ const processedContent = content;
29465
+ const lines = processedContent.split("\n");
29006
29466
  const formattedLines = [];
29007
29467
  let inList = false;
29008
29468
  for (let i = 0; i < lines.length; i++) {
@@ -29016,6 +29476,18 @@ var AIAgentView = () => {
29016
29476
  formattedLines.push("<br/>");
29017
29477
  continue;
29018
29478
  }
29479
+ if (trimmedLine.includes("|") && (trimmedLine.match(/\|/g) || []).length >= 1) {
29480
+ if (inList) {
29481
+ formattedLines.push("</ul>");
29482
+ inList = false;
29483
+ }
29484
+ const tableResult = parseTableFromText(lines, i);
29485
+ if (tableResult.html) {
29486
+ formattedLines.push(tableResult.html);
29487
+ i = tableResult.endIndex - 1;
29488
+ continue;
29489
+ }
29490
+ }
29019
29491
  if (trimmedLine.startsWith("###")) {
29020
29492
  if (inList) {
29021
29493
  formattedLines.push("</ul>");
@@ -29099,100 +29571,200 @@ var AIAgentView = () => {
29099
29571
  position: messages.length
29100
29572
  });
29101
29573
  }
29102
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-screen bg-white", children: [
29103
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${isSidebarOpen ? "w-80" : "w-0"} transition-all duration-300 overflow-hidden flex-shrink-0`, children: /* @__PURE__ */ jsxRuntime.jsx(
29104
- ThreadSidebar,
29574
+ const renderAssistantContent = (content) => {
29575
+ return /* @__PURE__ */ jsxRuntime.jsx(
29576
+ "div",
29105
29577
  {
29106
- activeThreadId,
29107
- onSelectThread: setActiveThreadId,
29108
- onNewThread: handleNewThread,
29109
- className: "h-full"
29578
+ className: "formatted-content",
29579
+ dangerouslySetInnerHTML: { __html: formatMessage(content) }
29110
29580
  }
29111
- ) }),
29112
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col", children: [
29113
- /* @__PURE__ */ jsxRuntime.jsx("header", { className: "flex-shrink-0 bg-white border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
29114
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
29115
- /* @__PURE__ */ jsxRuntime.jsx(
29116
- "button",
29117
- {
29118
- onClick: () => setIsSidebarOpen(!isSidebarOpen),
29119
- className: "p-2 hover:bg-gray-100 rounded-lg transition-colors",
29120
- "aria-label": "Toggle sidebar",
29121
- children: isSidebarOpen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-5 h-5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "w-5 h-5" })
29581
+ );
29582
+ };
29583
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-screen bg-white", children: [
29584
+ /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: {
29585
+ __html: `
29586
+ @keyframes slideDown {
29587
+ 0% {
29588
+ transform: translateY(-40vh);
29589
+ opacity: 1;
29122
29590
  }
29123
- ),
29124
- /* @__PURE__ */ jsxRuntime.jsxs(
29125
- "button",
29126
- {
29127
- onClick: () => navigate("/"),
29128
- className: "flex items-center gap-2 text-gray-600 hover:text-gray-900 transition-colors",
29129
- "aria-label": "Go back",
29130
- children: [
29131
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "w-5 h-5" }),
29132
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Back" })
29133
- ]
29591
+ 100% {
29592
+ transform: translateY(0);
29593
+ opacity: 1;
29134
29594
  }
29135
- )
29136
- ] }),
29595
+ }
29596
+ `
29597
+ } }),
29598
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex-1 flex flex-col h-screen transition-all duration-300 ${isSidebarOpen ? "mr-80" : "mr-0"}`, children: [
29599
+ /* @__PURE__ */ jsxRuntime.jsx("header", { className: "flex-shrink-0 bg-white border-b border-gray-200 sticky top-0 z-10", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
29600
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
29601
+ "button",
29602
+ {
29603
+ onClick: () => navigate("/"),
29604
+ className: "flex items-center gap-2 text-gray-600 hover:text-gray-900 transition-colors",
29605
+ "aria-label": "Go back",
29606
+ children: [
29607
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "w-5 h-5" }),
29608
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: "Back" })
29609
+ ]
29610
+ }
29611
+ ) }),
29137
29612
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
29138
29613
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
29139
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 tracking-tight leading-none", children: "Axel - AI Manufacturing Expert" }),
29614
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-xl sm:text-2xl md:text-3xl font-bold text-gray-800 tracking-tight leading-none", children: "Chat with Axel" }),
29140
29615
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rounded-full bg-blue-500 animate-pulse ring-2 ring-blue-500/30" })
29141
29616
  ] }),
29142
29617
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center mt-1", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-gray-500", children: /* @__PURE__ */ jsxRuntime.jsx(ISTTimer_default, {}) }) })
29143
29618
  ] }),
29144
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-24" })
29619
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxRuntime.jsxs(
29620
+ "button",
29621
+ {
29622
+ onClick: () => setIsSidebarOpen(!isSidebarOpen),
29623
+ className: "relative p-2 hover:bg-gray-100 rounded-lg transition-colors",
29624
+ "aria-label": "Toggle sidebar",
29625
+ children: [
29626
+ isSidebarOpen ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-5 h-5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "w-5 h-5" }),
29627
+ !isSidebarOpen && newChatCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center font-medium", children: newChatCount })
29628
+ ]
29629
+ }
29630
+ ) })
29145
29631
  ] }) }) }),
29146
29632
  /* @__PURE__ */ jsxRuntime.jsx(
29147
29633
  "main",
29148
29634
  {
29149
29635
  ref: containerRef,
29150
- className: "flex-1 overflow-y-auto bg-gray-50/50 min-h-0",
29151
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-5xl mx-auto p-4 sm:p-6 md:p-8", children: [
29152
- displayMessages.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-8 bg-white border border-gray-200/80 shadow-sm rounded-2xl p-6 sm:p-8", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-4", children: [
29153
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-xl overflow-hidden shadow-sm flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
29154
- "img",
29155
- {
29156
- src: axelProfilePng,
29157
- alt: "Axel - AI Manufacturing Expert",
29158
- className: "w-full h-full object-cover"
29159
- }
29160
- ) }),
29161
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 pt-1", children: [
29162
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-xl font-semibold text-gray-900 mb-2", children: "Hi, I'm Axel - Your AI Supervisor" }),
29163
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-600 leading-relaxed", children: "Ask me anything about your shop-floor. I'll remember our conversation and help you optimize your manufacturing processes." })
29636
+ className: `flex-1 bg-gray-50/50 min-h-0 ${displayMessages.length === 0 && !isTransitioning ? "flex items-center justify-center" : "overflow-y-auto"}`,
29637
+ children: !activeThreadId && displayMessages.length === 0 && !isTransitioning ? (
29638
+ /* Centered welcome and input for new chat */
29639
+ /* @__PURE__ */ jsxRuntime.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: [
29640
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
29641
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center mb-8", children: /* @__PURE__ */ jsxRuntime.jsx(ProfilePicture, { alt: "Axel - AI Manufacturing Expert", className: "w-24 h-24" }) }),
29642
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { className: "text-3xl font-semibold text-gray-900", children: [
29643
+ typedText,
29644
+ typedText.length < "Hi, I'm Axel - Your AI Supervisor".length && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "animate-pulse", children: "|" })
29645
+ ] })
29646
+ ] }),
29647
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full max-w-2xl", children: [
29648
+ activeThreadId && messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
29649
+ "button",
29650
+ {
29651
+ onClick: handleNewThread,
29652
+ className: "inline-flex items-center gap-2 px-3 py-1.5 text-xs font-medium text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-lg transition-colors",
29653
+ children: [
29654
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "w-3.5 h-3.5" }),
29655
+ "New conversation"
29656
+ ]
29657
+ }
29658
+ ) }),
29659
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [
29660
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative bg-white rounded-3xl shadow-lg border border-gray-200 focus-within:border-gray-300 transition-all duration-200", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-end gap-2 p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 relative", children: [
29661
+ /* @__PURE__ */ jsxRuntime.jsx(
29662
+ "textarea",
29663
+ {
29664
+ ref: textareaRef,
29665
+ value: inputValue,
29666
+ onChange: (e) => {
29667
+ const newValue = e.target.value;
29668
+ setInputValue(newValue);
29669
+ if (newValue.length > 0 && !hasStartedTyping) {
29670
+ trackTypingStart();
29671
+ }
29672
+ if (newValue.length > 0) {
29673
+ trackTypingProgress(newValue);
29674
+ }
29675
+ if (newValue.length === 0) {
29676
+ resetTypingState();
29677
+ }
29678
+ },
29679
+ onKeyDown: handleKeyDown,
29680
+ onFocus: () => {
29681
+ trackCoreEvent("AI Agent Input Focused", {
29682
+ line_id: lineId,
29683
+ company_id: companyId,
29684
+ shift_id: shiftId,
29685
+ active_thread_id: activeThreadId,
29686
+ has_existing_messages: messages.length > 0,
29687
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
29688
+ });
29689
+ },
29690
+ placeholder: "Ask me about production optimization, quality metrics, or any manufacturing challenge...",
29691
+ 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",
29692
+ rows: 1,
29693
+ style: { minHeight: "24px", maxHeight: "120px" }
29694
+ }
29695
+ ),
29696
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-2 bottom-2 flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
29697
+ "button",
29698
+ {
29699
+ type: "submit",
29700
+ disabled: !inputValue.trim() || isCurrentThreadLoading,
29701
+ className: "inline-flex items-center justify-center w-8 h-8 bg-gray-900 text-white rounded-full hover:bg-gray-800 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-gray-500/20",
29702
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "w-4 h-4" })
29703
+ }
29704
+ ) })
29705
+ ] }) }) }),
29706
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center mt-2 text-xs text-gray-400", children: [
29707
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: isCurrentThreadLoading ? "You can type your next message while Axel responds" : "Press Enter to send \u2022 Shift+Enter for new line" }),
29708
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 ml-4", children: [
29709
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-1.5 h-1.5 rounded-full ${isCurrentThreadLoading ? "bg-orange-500" : "bg-green-500"}` }),
29710
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: isCurrentThreadLoading ? "Responding..." : "Connected" })
29711
+ ] })
29712
+ ] })
29713
+ ] })
29164
29714
  ] })
29165
- ] }) }),
29166
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
29715
+ ] })
29716
+ ) : isTransitioning ? (
29717
+ /* Transition state - show user message first, then thinking */
29718
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 py-6 pb-32", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
29167
29719
  displayMessages.map((message, index) => /* @__PURE__ */ jsxRuntime.jsxs(
29168
29720
  "div",
29169
29721
  {
29170
29722
  className: `flex gap-4 ${message.role === "user" ? "justify-end" : "justify-start"}`,
29171
29723
  children: [
29172
- message.role === "assistant" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 rounded-xl overflow-hidden shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx(
29173
- "img",
29724
+ message.role === "assistant" && /* @__PURE__ */ jsxRuntime.jsx(ProfilePicture, {}),
29725
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `max-w-none w-full group ${message.role === "user" ? "order-1" : ""}`, children: /* @__PURE__ */ jsxRuntime.jsx(
29726
+ "div",
29174
29727
  {
29175
- src: axelProfilePng,
29176
- alt: "Axel",
29177
- className: "w-full h-full object-cover"
29728
+ className: `relative px-5 py-4 rounded-2xl shadow-sm ${message.role === "user" ? "bg-blue-600 text-white max-w-[85%] ml-auto" : "bg-white border border-gray-200/80 max-w-full"}`,
29729
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${message.role === "user" ? "text-white" : "text-gray-800"}`, children: [
29730
+ message.role === "assistant" ? renderAssistantContent(message.content) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap leading-relaxed", children: message.content }),
29731
+ message.id === -1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-0.5 h-4 bg-gray-400 animate-pulse ml-0.5" })
29732
+ ] })
29178
29733
  }
29179
- ) }) }),
29180
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `max-w-[75%] group ${message.role === "user" ? "order-1" : ""}`, children: [
29734
+ ) })
29735
+ ]
29736
+ },
29737
+ message.id === -1 ? "streaming-message" : `${message.id}-${index}`
29738
+ )),
29739
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 justify-start", children: [
29740
+ /* @__PURE__ */ jsxRuntime.jsx(ProfilePicture, {}),
29741
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white border border-gray-200/80 px-5 py-4 rounded-2xl shadow-sm max-w-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
29742
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex space-x-1", children: [
29743
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-bounce" }),
29744
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-bounce", style: { animationDelay: "0.1s" } }),
29745
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-bounce", style: { animationDelay: "0.2s" } })
29746
+ ] }),
29747
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600 text-sm", children: "Axel is thinking..." })
29748
+ ] }) })
29749
+ ] })
29750
+ ] }) })
29751
+ ) : (
29752
+ /* Regular chat view with messages */
29753
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-4xl mx-auto px-4 sm:px-6 py-6 pb-32", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
29754
+ displayMessages.map((message, index) => /* @__PURE__ */ jsxRuntime.jsxs(
29755
+ "div",
29756
+ {
29757
+ className: `flex gap-4 ${message.role === "user" ? "justify-end" : "justify-start"}`,
29758
+ children: [
29759
+ message.role === "assistant" && /* @__PURE__ */ jsxRuntime.jsx(ProfilePicture, {}),
29760
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `max-w-none w-full group ${message.role === "user" ? "order-1" : ""}`, children: [
29181
29761
  /* @__PURE__ */ jsxRuntime.jsxs(
29182
29762
  "div",
29183
29763
  {
29184
- className: `relative px-5 py-4 rounded-2xl shadow-sm ${message.role === "user" ? "bg-blue-600 text-white" : "bg-white border border-gray-200/80"}`,
29764
+ className: `relative px-5 py-4 rounded-2xl shadow-sm ${message.role === "user" ? "bg-blue-600 text-white max-w-[85%] ml-auto" : "bg-white border border-gray-200/80 max-w-full"}`,
29185
29765
  children: [
29186
29766
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${message.role === "user" ? "text-white" : "text-gray-800"}`, children: [
29187
- message.role === "assistant" ? /* @__PURE__ */ jsxRuntime.jsx(
29188
- "div",
29189
- {
29190
- className: "formatted-content",
29191
- dangerouslySetInnerHTML: {
29192
- __html: formatMessage(message.content)
29193
- }
29194
- }
29195
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap leading-relaxed", children: message.content }),
29767
+ message.role === "assistant" ? renderAssistantContent(message.content) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap leading-relaxed", children: message.content }),
29196
29768
  message.id === -1 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block w-0.5 h-4 bg-gray-400 animate-pulse ml-0.5" })
29197
29769
  ] }),
29198
29770
  message.role === "assistant" && message.id !== -1 && /* @__PURE__ */ jsxRuntime.jsx(
@@ -29218,15 +29790,14 @@ var AIAgentView = () => {
29218
29790
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Axel" })
29219
29791
  ] })
29220
29792
  ] })
29221
- ] }),
29222
- message.role === "user" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 bg-gray-700 text-white rounded-xl flex items-center justify-center text-sm font-semibold", children: entityConfig.companyId?.charAt(0).toUpperCase() || "U" }) })
29793
+ ] })
29223
29794
  ]
29224
29795
  },
29225
- message.id === -1 ? `streaming-${currentStreaming.message.length}` : `${message.id}-${index}`
29796
+ message.id === -1 ? "streaming-message" : `${message.id}-${index}`
29226
29797
  )),
29227
29798
  lastError && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 justify-start", children: [
29228
29799
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 rounded-xl bg-red-100 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-6 h-6 text-red-600" }) }) }),
29229
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-red-50 border border-red-200 px-5 py-4 rounded-2xl shadow-sm max-w-[75%]", children: [
29800
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "bg-red-50 border border-red-200 px-5 py-4 rounded-2xl shadow-sm max-w-full", children: [
29230
29801
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-red-800 text-sm", children: lastError }),
29231
29802
  /* @__PURE__ */ jsxRuntime.jsx(
29232
29803
  "button",
@@ -29239,29 +29810,22 @@ var AIAgentView = () => {
29239
29810
  ] })
29240
29811
  ] }),
29241
29812
  isCurrentThreadLoading && !currentStreaming.message && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 justify-start", children: [
29242
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-12 h-12 rounded-xl overflow-hidden shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsx(
29243
- "img",
29244
- {
29245
- src: axelProfilePng,
29246
- alt: "Axel",
29247
- className: "w-full h-full object-cover"
29248
- }
29249
- ) }) }),
29250
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white border border-gray-200/80 px-5 py-4 rounded-2xl shadow-sm max-w-[75%]", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
29813
+ /* @__PURE__ */ jsxRuntime.jsx(ProfilePicture, {}),
29814
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-white border border-gray-200/80 px-5 py-4 rounded-2xl shadow-sm max-w-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
29251
29815
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex space-x-1", children: [
29252
29816
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-bounce" }),
29253
29817
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-bounce", style: { animationDelay: "0.1s" } }),
29254
29818
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-bounce", style: { animationDelay: "0.2s" } })
29255
29819
  ] }),
29256
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600 text-sm", children: "Axel is analyzing your request..." })
29820
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-600 text-sm", children: "Axel is thinking..." })
29257
29821
  ] }) })
29258
29822
  ] }),
29259
29823
  /* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
29260
- ] })
29261
- ] })
29824
+ ] }) })
29825
+ )
29262
29826
  }
29263
29827
  ),
29264
- /* @__PURE__ */ jsxRuntime.jsx("footer", { className: "flex-shrink-0 border-t border-gray-200 bg-white sticky bottom-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-5xl mx-auto p-4 sm:p-6", children: [
29828
+ (displayMessages.length > 0 || isTransitioning) && /* @__PURE__ */ jsxRuntime.jsx("footer", { className: "fixed bottom-0 left-0 right-0 bg-gradient-to-t from-gray-50/50 to-transparent pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-4xl mx-auto p-4 sm:p-6 pointer-events-auto", children: [
29265
29829
  activeThreadId && messages.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
29266
29830
  "button",
29267
29831
  {
@@ -29273,48 +29837,81 @@ var AIAgentView = () => {
29273
29837
  ]
29274
29838
  }
29275
29839
  ) }),
29276
- /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-3", children: [
29277
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-3 items-end", children: [
29278
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 relative", children: [
29279
- /* @__PURE__ */ jsxRuntime.jsx(
29280
- "textarea",
29281
- {
29282
- ref: textareaRef,
29283
- value: inputValue,
29284
- onChange: (e) => setInputValue(e.target.value),
29285
- onKeyDown: handleKeyDown,
29286
- placeholder: "Ask me about production optimization, quality metrics, or any manufacturing challenge...",
29287
- className: "w-full resize-none rounded-xl border border-gray-300 px-4 py-3.5 pr-16 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-400 bg-white placeholder-gray-500 text-gray-900 text-sm leading-relaxed shadow-sm transition-all duration-200",
29288
- rows: 1,
29289
- disabled: isCurrentThreadLoading,
29290
- style: { minHeight: "48px", maxHeight: "120px" }
29291
- }
29292
- ),
29293
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute bottom-3 right-3 flex items-center gap-2", children: inputValue.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-400 font-mono", children: inputValue.length }) })
29294
- ] }),
29295
- /* @__PURE__ */ jsxRuntime.jsxs(
29296
- "button",
29297
- {
29298
- type: "submit",
29299
- disabled: !inputValue.trim() || isCurrentThreadLoading,
29300
- className: "inline-flex items-center justify-center gap-2 h-12 px-5 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all duration-200 text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:ring-offset-2 shadow-sm hover:shadow-md disabled:hover:shadow-sm",
29301
- children: [
29302
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "w-4 h-4" }),
29303
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Send" })
29304
- ]
29305
- }
29306
- )
29307
- ] }),
29308
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs text-gray-400", children: [
29309
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Press Enter to send \u2022 Shift+Enter for new line" }) }),
29310
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
29311
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-1.5 h-1.5 bg-green-500 rounded-full" }),
29312
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Connected" })
29313
- ] }) })
29840
+ /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, children: [
29841
+ /* @__PURE__ */ jsxRuntime.jsx(
29842
+ "div",
29843
+ {
29844
+ className: `relative bg-white rounded-3xl shadow-lg border border-gray-200 focus-within:border-gray-300 transition-all duration-200 ${isTransitioning ? "animate-slide-down" : ""}`,
29845
+ style: isTransitioning ? {
29846
+ animation: "slideDown 0.8s cubic-bezier(0.4, 0, 0.2, 1) forwards"
29847
+ } : {},
29848
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-end gap-2 p-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 relative", children: [
29849
+ /* @__PURE__ */ jsxRuntime.jsx(
29850
+ "textarea",
29851
+ {
29852
+ ref: textareaRef,
29853
+ value: inputValue,
29854
+ onChange: (e) => {
29855
+ const newValue = e.target.value;
29856
+ setInputValue(newValue);
29857
+ if (newValue.length > 0 && !hasStartedTyping) {
29858
+ trackTypingStart();
29859
+ }
29860
+ if (newValue.length > 0) {
29861
+ trackTypingProgress(newValue);
29862
+ }
29863
+ if (newValue.length === 0) {
29864
+ resetTypingState();
29865
+ }
29866
+ },
29867
+ onKeyDown: handleKeyDown,
29868
+ onFocus: () => {
29869
+ trackCoreEvent("AI Agent Input Focused", {
29870
+ line_id: lineId,
29871
+ company_id: companyId,
29872
+ shift_id: shiftId,
29873
+ active_thread_id: activeThreadId,
29874
+ has_existing_messages: messages.length > 0,
29875
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
29876
+ });
29877
+ },
29878
+ placeholder: "Ask me about production optimization, quality metrics, or any manufacturing challenge...",
29879
+ 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",
29880
+ rows: 1,
29881
+ style: { minHeight: "24px", maxHeight: "120px" }
29882
+ }
29883
+ ),
29884
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-2 bottom-2 flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
29885
+ "button",
29886
+ {
29887
+ type: "submit",
29888
+ disabled: !inputValue.trim() || isCurrentThreadLoading,
29889
+ className: "inline-flex items-center justify-center w-8 h-8 bg-gray-900 text-white rounded-full hover:bg-gray-800 transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-gray-500/20",
29890
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "w-4 h-4" })
29891
+ }
29892
+ ) })
29893
+ ] }) })
29894
+ }
29895
+ ),
29896
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-center mt-2 text-xs text-gray-400", children: [
29897
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: isCurrentThreadLoading ? "You can type your next message while Axel responds" : "Press Enter to send \u2022 Shift+Enter for new line" }),
29898
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 ml-4", children: [
29899
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-1.5 h-1.5 rounded-full ${isCurrentThreadLoading ? "bg-orange-500" : "bg-green-500"}` }),
29900
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: isCurrentThreadLoading ? "Responding..." : "Connected" })
29901
+ ] })
29314
29902
  ] })
29315
29903
  ] })
29316
29904
  ] }) })
29317
- ] })
29905
+ ] }),
29906
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: `${isSidebarOpen ? "w-80" : "w-0"} transition-all duration-300 overflow-hidden flex-shrink-0 border-l border-gray-200 h-screen fixed right-0 top-0 z-20`, children: /* @__PURE__ */ jsxRuntime.jsx(
29907
+ ThreadSidebar,
29908
+ {
29909
+ activeThreadId,
29910
+ onSelectThread: setActiveThreadId,
29911
+ onNewThread: handleNewThread,
29912
+ className: "h-full"
29913
+ }
29914
+ ) })
29318
29915
  ] });
29319
29916
  };
29320
29917
  var AIAgentView_default = AIAgentView;
@@ -31191,7 +31788,8 @@ var parseBreaksFromDB = (dbBreaks) => {
31191
31788
  duration: calculateBreakDuration(
31192
31789
  breakItem.start || breakItem.startTime || "00:00",
31193
31790
  breakItem.end || breakItem.endTime || "00:00"
31194
- )
31791
+ ),
31792
+ remarks: breakItem.remarks || breakItem.name || ""
31195
31793
  }));
31196
31794
  } else if (dbBreaks.breaks && Array.isArray(dbBreaks.breaks)) {
31197
31795
  return dbBreaks.breaks.map((breakItem) => ({
@@ -31200,7 +31798,8 @@ var parseBreaksFromDB = (dbBreaks) => {
31200
31798
  duration: calculateBreakDuration(
31201
31799
  breakItem.start || breakItem.startTime || "00:00",
31202
31800
  breakItem.end || breakItem.endTime || "00:00"
31203
- )
31801
+ ),
31802
+ remarks: breakItem.remarks || breakItem.name || ""
31204
31803
  }));
31205
31804
  } else {
31206
31805
  console.warn("Unexpected breaks format:", dbBreaks);
@@ -31218,7 +31817,8 @@ var formatBreaks = (breaks) => {
31218
31817
  return {
31219
31818
  breaks: breaks.map((breakItem) => ({
31220
31819
  start: breakItem.startTime,
31221
- end: breakItem.endTime
31820
+ end: breakItem.endTime,
31821
+ remarks: breakItem.remarks || ""
31222
31822
  }))
31223
31823
  };
31224
31824
  };
@@ -31228,13 +31828,14 @@ var BreakRow = React46.memo(({
31228
31828
  onRemove,
31229
31829
  index
31230
31830
  }) => {
31231
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-9 gap-2 sm:gap-4 items-center w-full bg-white hover:bg-gray-50 rounded-md transition-all duration-200 p-2", children: [
31831
+ return /* @__PURE__ */ jsxRuntime.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: [
31232
31832
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
31233
31833
  "input",
31234
31834
  {
31235
31835
  type: "time",
31236
31836
  value: breakItem.startTime,
31237
31837
  onChange: (e) => onUpdate(index, "startTime", e.target.value),
31838
+ step: "60",
31238
31839
  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"
31239
31840
  }
31240
31841
  ) }),
@@ -31244,6 +31845,7 @@ var BreakRow = React46.memo(({
31244
31845
  type: "time",
31245
31846
  value: breakItem.endTime,
31246
31847
  onChange: (e) => onUpdate(index, "endTime", e.target.value),
31848
+ step: "60",
31247
31849
  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"
31248
31850
  }
31249
31851
  ) }),
@@ -31251,6 +31853,16 @@ var BreakRow = React46.memo(({
31251
31853
  breakItem.duration,
31252
31854
  " min"
31253
31855
  ] }) }),
31856
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: /* @__PURE__ */ jsxRuntime.jsx(
31857
+ "input",
31858
+ {
31859
+ type: "text",
31860
+ value: breakItem.remarks || "",
31861
+ onChange: (e) => onUpdate(index, "remarks", e.target.value),
31862
+ placeholder: "Break remarks",
31863
+ 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"
31864
+ }
31865
+ ) }),
31254
31866
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-1 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
31255
31867
  "button",
31256
31868
  {
@@ -31367,10 +31979,11 @@ var ShiftPanel = React46.memo(({
31367
31979
  "Breaks"
31368
31980
  ] }) }),
31369
31981
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 mb-4 w-full", children: breaks.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
31370
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-9 gap-2 sm:gap-4 text-xs font-medium text-gray-500 mb-1 w-full px-2", children: [
31982
+ /* @__PURE__ */ jsxRuntime.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: [
31371
31983
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: "Break Start" }),
31372
31984
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: "Break End" }),
31373
31985
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-2", children: "Duration" }),
31986
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-3", children: "Remarks" }),
31374
31987
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-1" })
31375
31988
  ] }),
31376
31989
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-gray-50/80 p-2 rounded-md", children: breaks.map((breakItem, index) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -31583,7 +32196,8 @@ var ShiftsView = ({
31583
32196
  const newBreak = {
31584
32197
  startTime: dayShift.startTime,
31585
32198
  endTime: dayShift.startTime,
31586
- duration: 0
32199
+ duration: 0,
32200
+ remarks: ""
31587
32201
  };
31588
32202
  return {
31589
32203
  ...typedConfig,
@@ -31624,14 +32238,16 @@ var ShiftsView = ({
31624
32238
  const dayShift = { ...typedConfig.dayShift };
31625
32239
  const newBreaks = [...dayShift.breaks];
31626
32240
  newBreaks[index] = { ...newBreaks[index], [field]: value };
31627
- const startParts = newBreaks[index].startTime.split(":").map(Number);
31628
- const endParts = newBreaks[index].endTime.split(":").map(Number);
31629
- let startMinutes = startParts[0] * 60 + startParts[1];
31630
- let endMinutes = endParts[0] * 60 + endParts[1];
31631
- if (endMinutes < startMinutes) {
31632
- endMinutes += 24 * 60;
31633
- }
31634
- newBreaks[index].duration = endMinutes - startMinutes;
32241
+ if (field === "startTime" || field === "endTime") {
32242
+ const startParts = newBreaks[index].startTime.split(":").map(Number);
32243
+ const endParts = newBreaks[index].endTime.split(":").map(Number);
32244
+ let startMinutes = startParts[0] * 60 + startParts[1];
32245
+ let endMinutes = endParts[0] * 60 + endParts[1];
32246
+ if (endMinutes < startMinutes) {
32247
+ endMinutes += 24 * 60;
32248
+ }
32249
+ newBreaks[index].duration = endMinutes - startMinutes;
32250
+ }
31635
32251
  return {
31636
32252
  ...typedConfig,
31637
32253
  dayShift: {
@@ -31650,14 +32266,16 @@ var ShiftsView = ({
31650
32266
  const nightShift = { ...typedConfig.nightShift };
31651
32267
  const newBreaks = [...nightShift.breaks];
31652
32268
  newBreaks[index] = { ...newBreaks[index], [field]: value };
31653
- const startParts = newBreaks[index].startTime.split(":").map(Number);
31654
- const endParts = newBreaks[index].endTime.split(":").map(Number);
31655
- let startMinutes = startParts[0] * 60 + startParts[1];
31656
- let endMinutes = endParts[0] * 60 + endParts[1];
31657
- if (endMinutes < startMinutes) {
31658
- endMinutes += 24 * 60;
31659
- }
31660
- newBreaks[index].duration = endMinutes - startMinutes;
32269
+ if (field === "startTime" || field === "endTime") {
32270
+ const startParts = newBreaks[index].startTime.split(":").map(Number);
32271
+ const endParts = newBreaks[index].endTime.split(":").map(Number);
32272
+ let startMinutes = startParts[0] * 60 + startParts[1];
32273
+ let endMinutes = endParts[0] * 60 + endParts[1];
32274
+ if (endMinutes < startMinutes) {
32275
+ endMinutes += 24 * 60;
32276
+ }
32277
+ newBreaks[index].duration = endMinutes - startMinutes;
32278
+ }
31661
32279
  return {
31662
32280
  ...typedConfig,
31663
32281
  nightShift: {