impact-chatbot 2.3.51 → 2.3.52

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.esm.js CHANGED
@@ -18,7 +18,7 @@ import remarkBreaks from 'remark-breaks';
18
18
  import remarkGfm from 'remark-gfm';
19
19
  import DOMPurify from 'dompurify';
20
20
  import { setSelectedFilters, setFilterConfiguration, getFilterUserConfiguration } from 'core/actions/filterAction';
21
- import { setChatbotContext, setStepFormStreamData, setPersistedFormValues, clearPersistedFormValues, setCurrentAgentChatId, setThinkingContext, setHierarchyKeyValue, setSavedFilterSets } from 'core/actions/smartBotActions';
21
+ import { setChatbotContext, setStepFormStreamData, setThinkingContext, setPersistedFormValues, clearPersistedFormValues, setCurrentAgentChatId, setHierarchyKeyValue, setSavedFilterSets } from 'core/actions/smartBotActions';
22
22
  import { useNavigate, useLocation } from 'react-router-dom-v5-compat';
23
23
  import RefreshIcon from '@mui/icons-material/Refresh';
24
24
  import styled from 'styled-components';
@@ -1277,12 +1277,70 @@ const ThinkingIndicator = (props) => {
1277
1277
  return (jsx("div", { children: jsx("div", { children: thinkingContentState ? (jsx("div", { children: jsx(TextRenderer, { text: thinkingContentState, thinking: true }) })) : (jsxs("div", { className: classes.thinkingSpinner, children: [jsx("span", { className: classes.thinkingDot }), jsx("span", { className: classes.thinkingDot }), jsx("span", { className: classes.thinkingDot })] })) }) }));
1278
1278
  };
1279
1279
 
1280
+ /**
1281
+ * Formats elapsed seconds into "Xm:Ys" display string.
1282
+ * e.g. 0 -> "0m:00s", 65 -> "1m:05s", 130 -> "2m:10s"
1283
+ */
1284
+ const formatElapsedTime = (totalSeconds) => {
1285
+ const minutes = Math.floor(totalSeconds / 60);
1286
+ const seconds = totalSeconds % 60;
1287
+ return `${minutes}m:${String(seconds).padStart(2, '0')}s`;
1288
+ };
1280
1289
  const ThinkinHeaderInfo = (props) => {
1281
1290
  const thinkingContext = useSelector((state) => {
1282
1291
  return state.smartBotReducer.thinkingContext;
1283
1292
  });
1284
- const { thinkingHeaderMessage } = thinkingContext;
1285
- return (jsx("div", { className: "thinkin-header-info", children: thinkingHeaderMessage }));
1293
+ const { streamStartTime, isStreamCompleted, finalElapsedSeconds, thinkingHeaderMessage } = thinkingContext;
1294
+ const [elapsed, setElapsed] = useState(0);
1295
+ const intervalRef = useRef(null);
1296
+ // Once this instance sees "completed", freeze it permanently so a future
1297
+ // chat's streamStartTime doesn't cause it to start ticking again.
1298
+ const frozenRef = useRef(false);
1299
+ const frozenTextRef = useRef(null);
1300
+ useEffect(() => {
1301
+ // If already frozen, ignore all future Redux changes
1302
+ if (frozenRef.current)
1303
+ return;
1304
+ // Clear any existing interval
1305
+ if (intervalRef.current) {
1306
+ clearInterval(intervalRef.current);
1307
+ intervalRef.current = null;
1308
+ }
1309
+ if (isStreamCompleted && typeof finalElapsedSeconds === 'number') {
1310
+ // Freeze this instance — it will never tick again
1311
+ frozenRef.current = true;
1312
+ frozenTextRef.current = `Completed in ${formatElapsedTime(finalElapsedSeconds)}`;
1313
+ setElapsed(finalElapsedSeconds);
1314
+ }
1315
+ else if (streamStartTime && !isStreamCompleted) {
1316
+ // Live timer — update every second
1317
+ const tick = () => {
1318
+ const now = Date.now();
1319
+ const diffSeconds = Math.floor((now - streamStartTime) / 1000);
1320
+ setElapsed(diffSeconds);
1321
+ };
1322
+ tick(); // immediate first tick
1323
+ intervalRef.current = setInterval(tick, 1000);
1324
+ }
1325
+ return () => {
1326
+ if (intervalRef.current) {
1327
+ clearInterval(intervalRef.current);
1328
+ intervalRef.current = null;
1329
+ }
1330
+ };
1331
+ }, [streamStartTime, isStreamCompleted, finalElapsedSeconds]);
1332
+ // Determine display text
1333
+ let displayText = thinkingHeaderMessage || '';
1334
+ if (frozenRef.current && frozenTextRef.current) {
1335
+ displayText = frozenTextRef.current;
1336
+ }
1337
+ else if (streamStartTime && !isStreamCompleted) {
1338
+ displayText = `Working for ${formatElapsedTime(elapsed)}`;
1339
+ }
1340
+ else if (isStreamCompleted && typeof finalElapsedSeconds === 'number') {
1341
+ displayText = `Completed in ${formatElapsedTime(finalElapsedSeconds)}`;
1342
+ }
1343
+ return (jsx("div", { className: "thinkin-header-info", children: displayText }));
1286
1344
  };
1287
1345
 
1288
1346
  // import { exampleGraphData } from "../examples/GraphExample";
@@ -4052,7 +4110,7 @@ const useChatState = () => {
4052
4110
  const [chatId, setChatId] = useState("");
4053
4111
  const [isStop, setIsStop] = useState(false);
4054
4112
  const [functionsState, setFunctionsState] = useState({});
4055
- const [thinkingHeaderMessage, setThinkingHeaderMessage] = useState("Planning next moves...");
4113
+ const [thinkingHeaderMessage, setThinkingHeaderMessage] = useState("Working… 0m:00s");
4056
4114
  const [legacyAgentScreen, setLegacyAgentScreen] = useState(false);
4057
4115
  const [uniqueChatId, setUniqueChatId] = useState("");
4058
4116
  const [fieldNumber, setFieldNumber] = useState(0);
@@ -5386,6 +5444,7 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5386
5444
  status: "",
5387
5445
  });
5388
5446
  const chatbotContext = useSelector((state) => state.smartBotReducer.chatbotContext);
5447
+ const thinkingContext = useSelector((state) => state.smartBotReducer.thinkingContext);
5389
5448
  const handleButtonClick = (button) => {
5390
5449
  if (isStepFormSubmit) {
5391
5450
  // Build the user_input from chatbotContext
@@ -5400,7 +5459,6 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5400
5459
  : typeof value === 'number' ? value : String(value);
5401
5460
  }
5402
5461
  }
5403
- console.log("[ButtonContent] step form submit, calling init API with userInput:", userInput);
5404
5462
  dispatch(setChatbotContext({}));
5405
5463
  // Call the init API via SSE directly
5406
5464
  callInitApiStream(userInput);
@@ -5459,6 +5517,9 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5459
5517
  dispatch(setStepFormStreamData({ status: "streaming_start", chunks: [], sessionId }));
5460
5518
  // Fire DOM event so SmartBot can show stop icon (Redux gets cleared by TabularContent before parent effects run)
5461
5519
  window.dispatchEvent(new CustomEvent("stepFormStreamStart"));
5520
+ // Capture start time for computing elapsed on completion.
5521
+ // Timer keeps running from the original streamStartTime (set when first API was hit).
5522
+ const stepFormStreamStartTime = Date.now();
5462
5523
  sourceRef.current = AxiosSource(endPoint, {
5463
5524
  method: "POST",
5464
5525
  headers: {
@@ -5473,7 +5534,6 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5473
5534
  sourceRef.current.addEventListener("message", (event) => {
5474
5535
  try {
5475
5536
  const data = JSON.parse(event.data);
5476
- console.log("[ButtonContent] SSE chunk:", data);
5477
5537
  // Always keep stepFormStreamControl.sessionId in sync with the latest chunk's session_id
5478
5538
  if (data?.session_id) {
5479
5539
  stepFormStreamControl.sessionId = data.session_id;
@@ -5514,6 +5574,22 @@ const ButtonContent = ({ bodyText, isFormDisabled = false, isStepFormSubmit = fa
5514
5574
  stepFormStreamControl.isStreaming = false;
5515
5575
  stepFormStreamControl.abort = null;
5516
5576
  window.dispatchEvent(new CustomEvent("stepFormStreamEnd"));
5577
+ // Stop the timer only when status:"completed" is received
5578
+ if (data?.status === "completed") {
5579
+ // Use the original streamStartTime from Redux (set when first API was hit)
5580
+ const originalStartTime = thinkingContext?.streamStartTime || stepFormStreamStartTime;
5581
+ const totalElapsed = Math.floor((Date.now() - originalStartTime) / 1000);
5582
+ const sfMin = Math.floor(totalElapsed / 60);
5583
+ const sfSec = totalElapsed % 60;
5584
+ const sfWorkedLabel = `Completed in ${sfMin}m:${String(sfSec).padStart(2, '0')}s`;
5585
+ dispatch(setThinkingContext({
5586
+ streamStartTime: originalStartTime,
5587
+ isStreamCompleted: true,
5588
+ finalElapsedSeconds: totalElapsed,
5589
+ thinkingHeaderMessage: sfWorkedLabel,
5590
+ thinkingContent: "",
5591
+ }));
5592
+ }
5517
5593
  // Signal tab switch to agent_response when status is "completed",
5518
5594
  // but only if the response doesn't contain a new step_form that needs user interaction
5519
5595
  const hasStepForm = chunksRef.some((c) => c.status === "step_form");
@@ -6972,24 +7048,6 @@ function clearTabNotification() {
6972
7048
  * This survives the cloneDeep replacement of botData in SmartBot's useEffect.
6973
7049
  */
6974
7050
  const streamStateMap = new Map();
6975
- /**
6976
- * Formats thinking time to display minutes when appropriate
6977
- * @param {number} seconds - Time in seconds
6978
- * @returns {string} Formatted time string
6979
- */
6980
- const formatThinkingTime = (seconds) => {
6981
- if (seconds < 60) {
6982
- return `${seconds} second${seconds !== 1 ? 's' : ''}`;
6983
- }
6984
- const minutes = Math.floor(seconds / 60);
6985
- const remainingSeconds = seconds % 60;
6986
- if (remainingSeconds === 0) {
6987
- return `${minutes} minute${minutes !== 1 ? 's' : ''}`;
6988
- }
6989
- else {
6990
- return `${minutes} min ${remainingSeconds} sec`;
6991
- }
6992
- };
6993
7051
  /**
6994
7052
  * StreamedContent Component
6995
7053
  * Handles real-time streaming of chat messages with a typing-like effect
@@ -7053,7 +7111,8 @@ const StreamedContent = ({ botData }) => {
7053
7111
  useRef(""); // Tracks the last received message
7054
7112
  const thinkingStartTimeRef = useRef(null);
7055
7113
  const thinkingTimeFinalRef = useRef(0);
7056
- const thinkingHeaderMessageRef = useRef("Planning next moves.....");
7114
+ const thinkingHeaderMessageRef = useRef("Working for 0m:00s");
7115
+ const streamStartTimeRef = useRef(null);
7057
7116
  const thinkingDoneRef = useRef(false);
7058
7117
  const setThinkingContentRef = useRef(setThinkingContent); // Store current setThinkingContent function
7059
7118
  const streamTimeoutRef = useRef(null); // Ref for stream timeout
@@ -7119,6 +7178,17 @@ const StreamedContent = ({ botData }) => {
7119
7178
  ? botData?.utilityObject?.method
7120
7179
  : "PUT";
7121
7180
  delete botData.inputBody.chat_input;
7181
+ // Start the timer immediately when the API is called
7182
+ if (!streamStartTimeRef.current) {
7183
+ streamStartTimeRef.current = Date.now();
7184
+ dispatch(setThinkingContext({
7185
+ streamStartTime: streamStartTimeRef.current,
7186
+ isStreamCompleted: false,
7187
+ finalElapsedSeconds: null,
7188
+ thinkingHeaderMessage: "Working for 0m:00s",
7189
+ thinkingContent: "",
7190
+ }));
7191
+ }
7122
7192
  // Initialize SSE connection with API endpoint
7123
7193
  sourceRef.current = AxiosSource(endPoint, {
7124
7194
  method: method,
@@ -7207,7 +7277,7 @@ const StreamedContent = ({ botData }) => {
7207
7277
  setQuestionsStepsMap(cloneDeep(initialMap));
7208
7278
  }
7209
7279
  else if (data.status === "thinking") {
7210
- messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = "Planning next moves";
7280
+ messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = (jsx(ThinkinHeaderInfo, { thinkingHeaderMessage: "Working for 0m:00s" }));
7211
7281
  // Start thinking if not already started
7212
7282
  if (!isThinking) {
7213
7283
  setIsThinking(true);
@@ -7218,6 +7288,9 @@ const StreamedContent = ({ botData }) => {
7218
7288
  thinkingContentRef.current = thinkingContentRef.current + data.message;
7219
7289
  messageToStoreRef.current.chatData.thinkingResponse.thinkingStream = newValue;
7220
7290
  dispatch(setThinkingContext({
7291
+ streamStartTime: streamStartTimeRef.current,
7292
+ isStreamCompleted: false,
7293
+ finalElapsedSeconds: null,
7221
7294
  thinkingHeaderMessage: thinkingHeaderMessageRef.current,
7222
7295
  thinkingContent: thinkingContentRef.current,
7223
7296
  }));
@@ -7350,7 +7423,8 @@ const StreamedContent = ({ botData }) => {
7350
7423
  stepFormStreamControl.baseUrl = _burl;
7351
7424
  // If this is the [DONE] chunk, mark streaming as complete
7352
7425
  if (data.message === "[DONE]") {
7353
- setStepsDone(true);
7426
+ // Do NOT set stepsDone here — step_form [DONE] is not status:"completed".
7427
+ // stepsDone should only be true when status:"completed" arrives.
7354
7428
  setIsStreamingDone(true);
7355
7429
  const doneState = streamStateMap.get(streamKey);
7356
7430
  if (doneState)
@@ -7379,10 +7453,13 @@ const StreamedContent = ({ botData }) => {
7379
7453
  // Store thinking time in messageToStoreRef for persistence
7380
7454
  messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
7381
7455
  thinkingTimeFinalRef.current = finalThinkingTime;
7382
- setThinkingHeaderMessage(`Thought for ${formatThinkingTime(finalThinkingTime)}`);
7383
- thinkingHeaderMessageRef.current = `Thought for ${formatThinkingTime(finalThinkingTime)}`;
7456
+ // Keep timer running — do NOT set "Worked for" here.
7457
+ // Timer continues showing "Working for Xm:Ys" until status:"completed" arrives.
7384
7458
  dispatch(setThinkingContext({
7385
- thinkingHeaderMessage: `Thinking Completed`,
7459
+ streamStartTime: streamStartTimeRef.current,
7460
+ isStreamCompleted: false,
7461
+ finalElapsedSeconds: null,
7462
+ thinkingHeaderMessage: thinkingHeaderMessageRef.current,
7386
7463
  thinkingContent: thinkingContentRef.current,
7387
7464
  }));
7388
7465
  }
@@ -7496,11 +7573,31 @@ const StreamedContent = ({ botData }) => {
7496
7573
  if (currentMode === "agent") {
7497
7574
  setIsStop(false);
7498
7575
  // Use thinkingTime state instead of thinkingTimeFinalRef.current to ensure we have the correct value
7499
- thinkingTime || thinkingTimeFinalRef.current || 1;
7500
- messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = `Thinking Completed`;
7501
- // messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = `Thought for ${formatThinkingTime(
7502
- // finalThinkingTime
7503
- // )}`;
7576
+ const finalThinkingTime = thinkingTime || thinkingTimeFinalRef.current || 1;
7577
+ const completedElapsed = streamStartTimeRef.current
7578
+ ? Math.floor((Date.now() - streamStartTimeRef.current) / 1000)
7579
+ : finalThinkingTime;
7580
+ const compMin = Math.floor(completedElapsed / 60);
7581
+ const compSec = completedElapsed % 60;
7582
+ const workedForLabel = `Completed in ${compMin}m:${String(compSec).padStart(2, '0')}s`;
7583
+ // Only stop the timer if we got status:"completed" (stepsDone=true).
7584
+ // If stream ended with step_form [DONE], keep timer running for the next restream.
7585
+ if (stepsDone) {
7586
+ // Completed — use a static string so this chat's heading won't
7587
+ // re-animate when a future chat dispatches a new streamStartTime.
7588
+ messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = workedForLabel;
7589
+ dispatch(setThinkingContext({
7590
+ streamStartTime: streamStartTimeRef.current,
7591
+ isStreamCompleted: true,
7592
+ finalElapsedSeconds: completedElapsed,
7593
+ thinkingHeaderMessage: workedForLabel,
7594
+ thinkingContent: thinkingContentRef.current,
7595
+ }));
7596
+ }
7597
+ else {
7598
+ // step_form case: keep heading as the live ThinkinHeaderInfo component so the timer keeps ticking
7599
+ messageToStoreRef.current.chatData.thinkingResponse.thinkingHeading = (jsx(ThinkinHeaderInfo, { thinkingHeaderMessage: "Working for 0m:00s" }));
7600
+ }
7504
7601
  let actualResponse = cloneDeep(messageToStoreRef.current.chatData.thinkingResponse.thinkingStream);
7505
7602
  messageToStoreRef.current.chatData.thinkingResponse.thinkingContent = actualResponse;
7506
7603
  parseResponse(messageToStoreRef.current.chatData, "text");
@@ -7741,6 +7838,20 @@ const StreamedContent = ({ botData }) => {
7741
7838
  setShowThoughtDropdown(true);
7742
7839
  messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
7743
7840
  }
7841
+ // Stop the timer on abort
7842
+ const abortElapsed = streamStartTimeRef.current
7843
+ ? Math.floor((Date.now() - streamStartTimeRef.current) / 1000)
7844
+ : 0;
7845
+ const abortMin = Math.floor(abortElapsed / 60);
7846
+ const abortSec = abortElapsed % 60;
7847
+ const abortLabel = `Completed in ${abortMin}m:${String(abortSec).padStart(2, '0')}s`;
7848
+ dispatch(setThinkingContext({
7849
+ streamStartTime: streamStartTimeRef.current,
7850
+ isStreamCompleted: true,
7851
+ finalElapsedSeconds: abortElapsed,
7852
+ thinkingHeaderMessage: abortLabel,
7853
+ thinkingContent: thinkingContentRef.current,
7854
+ }));
7744
7855
  // If no content yet, show aborted message
7745
7856
  // if (isEmpty(messageToStoreRef.current.chatData?.response)) {
7746
7857
  messageToStoreRef.current.chatData.response =
@@ -11628,7 +11739,7 @@ const useConversationManagement = (chatDataRef, currentMode, activeConversationI
11628
11739
  thinkingStream: item.data,
11629
11740
  thinkingContent: item.data,
11630
11741
  thinkingTime: 1,
11631
- thinkingHeading: "Thinking Completed",
11742
+ thinkingHeading: "Completed in 0m:00s",
11632
11743
  };
11633
11744
  }
11634
11745
  else if (item.chatType === "steps") {
@@ -11823,7 +11934,9 @@ const useConversationManagement = (chatDataRef, currentMode, activeConversationI
11823
11934
  thinkingResponse: msg.thinkingResponse ? {
11824
11935
  thinkingStream: msg.thinkingResponse.thinkingStream,
11825
11936
  thinkingTime: msg.thinkingResponse.thinkingTime,
11826
- thinkingHeading: msg.thinkingResponse.thinkingHeading,
11937
+ thinkingHeading: typeof msg.thinkingResponse.thinkingHeading === 'string'
11938
+ ? msg.thinkingResponse.thinkingHeading
11939
+ : "Completed in 0m:00s",
11827
11940
  } : undefined,
11828
11941
  }
11829
11942
  }))
@@ -12599,7 +12712,10 @@ const SmartBot = (props) => {
12599
12712
  // if(!isEmpty(userInput)) {
12600
12713
  dispatch(setThinkingContext({
12601
12714
  thinkingContent: "",
12602
- thinkingHeaderMessage: "Planning next moves.....",
12715
+ thinkingHeaderMessage: "Working for 0m:00s",
12716
+ streamStartTime: Date.now(),
12717
+ isStreamCompleted: false,
12718
+ finalElapsedSeconds: null,
12603
12719
  }));
12604
12720
  // }
12605
12721
  prepareDataAndSendToAgent(data, true, {
@@ -12814,7 +12930,10 @@ const SmartBot = (props) => {
12814
12930
  setShowChatPlaceholder(true);
12815
12931
  dispatch(setThinkingContext({
12816
12932
  thinkingContent: "",
12817
- thinkingHeaderMessage: "Planning next moves.....",
12933
+ thinkingHeaderMessage: "Working for 0m:00s",
12934
+ streamStartTime: null,
12935
+ isStreamCompleted: false,
12936
+ finalElapsedSeconds: null,
12818
12937
  }));
12819
12938
  setInitValue(true);
12820
12939
  setSessionId("");