impact-chatbot 2.3.85 → 2.3.87

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.
@@ -1,5 +1,7 @@
1
- declare const ChipsContent: ({ bodyText, props }: {
1
+ declare const ChipsContent: ({ bodyText, props, botData, isFormDisabled }: {
2
2
  bodyText: any;
3
3
  props: any;
4
+ botData: any;
5
+ isFormDisabled?: boolean;
4
6
  }) => import("react/jsx-runtime").JSX.Element;
5
7
  export default ChipsContent;
package/dist/index.cjs.js CHANGED
@@ -1442,31 +1442,50 @@ const ThinkinHeaderInfo = (props) => {
1442
1442
  });
1443
1443
  const { streamStartTime, isStreamCompleted, finalElapsedSeconds, thinkingHeaderMessage } = thinkingContext;
1444
1444
  const [elapsed, setElapsed] = React.useState(0);
1445
+ const [frozenText, setFrozenText] = React.useState(null);
1445
1446
  const intervalRef = React.useRef(null);
1446
1447
  // Once this instance sees "completed", freeze it permanently so a future
1447
1448
  // chat's streamStartTime doesn't cause it to start ticking again.
1448
1449
  const frozenRef = React.useRef(false);
1449
1450
  const frozenTextRef = React.useRef(null);
1450
- const streamStartTimeRef = React.useRef(null); // Track streamStartTime changes to unfreeze
1451
+ // The streamStartTime this instance latched onto. Used to detect when a
1452
+ // different stream has taken over (e.g. the user asked a new question while
1453
+ // this message's step form was still waiting for input).
1454
+ const ownStartTimeRef = React.useRef(null);
1455
+ /**
1456
+ * Freezes this instance permanently on a final label and elapsed value.
1457
+ */
1458
+ const freeze = (text, seconds) => {
1459
+ frozenRef.current = true;
1460
+ frozenTextRef.current = text;
1461
+ setFrozenText(text);
1462
+ setElapsed(seconds);
1463
+ };
1451
1464
  React.useEffect(() => {
1452
1465
  // If already frozen (stream completed), NEVER unfreeze.
1453
1466
  // This prevents a new chat's streamStartTime from restarting this timer.
1454
1467
  if (frozenRef.current)
1455
1468
  return;
1456
- // Track streamStartTime changes for non-frozen instances
1457
- if (streamStartTimeRef.current !== streamStartTime) {
1458
- streamStartTimeRef.current = streamStartTime;
1469
+ // Adopt the first non-null streamStartTime we see as this instance's own
1470
+ if (ownStartTimeRef.current === null && streamStartTime) {
1471
+ ownStartTimeRef.current = streamStartTime;
1459
1472
  }
1473
+ const ownStartTime = ownStartTimeRef.current;
1460
1474
  // Clear any existing interval
1461
1475
  if (intervalRef.current) {
1462
1476
  clearInterval(intervalRef.current);
1463
1477
  intervalRef.current = null;
1464
1478
  }
1465
1479
  if (isStreamCompleted && typeof finalElapsedSeconds === 'number') {
1466
- // Freeze this instance it will never tick again
1467
- frozenRef.current = true;
1468
- frozenTextRef.current = `Completed in ${formatElapsedTime(finalElapsedSeconds)}`;
1469
- setElapsed(finalElapsedSeconds);
1480
+ // Freeze this instance - it will never tick again
1481
+ freeze(`Completed in ${formatElapsedTime(finalElapsedSeconds)}`, finalElapsedSeconds);
1482
+ }
1483
+ else if (ownStartTime !== null && streamStartTime !== ownStartTime) {
1484
+ // A different stream has started (new question) while this one never
1485
+ // completed - typically an abandoned step form. Freeze on this
1486
+ // instance's OWN elapsed time instead of following the new stream.
1487
+ const ownElapsed = Math.floor((Date.now() - ownStartTime) / 1000);
1488
+ freeze(`Completed in ${formatElapsedTime(ownElapsed)}`, ownElapsed);
1470
1489
  }
1471
1490
  else if (streamStartTime && !isStreamCompleted) {
1472
1491
  // Live timer — update every second
@@ -1487,8 +1506,8 @@ const ThinkinHeaderInfo = (props) => {
1487
1506
  }, [streamStartTime, isStreamCompleted, finalElapsedSeconds]);
1488
1507
  // Determine display text
1489
1508
  let displayText = thinkingHeaderMessage || '';
1490
- if (frozenRef.current && frozenTextRef.current) {
1491
- displayText = frozenTextRef.current;
1509
+ if (frozenText || (frozenRef.current && frozenTextRef.current)) {
1510
+ displayText = frozenText || frozenTextRef.current;
1492
1511
  }
1493
1512
  else if (streamStartTime && !isStreamCompleted) {
1494
1513
  displayText = `Working for ${formatElapsedTime(elapsed)}`;
@@ -2482,6 +2501,20 @@ const useChatFlow = (chatDataRef, setLoader, setFlowType, setScreenName, setUser
2482
2501
  }
2483
2502
  if (data.flow_type === "agent" && data.actionType === "direct") {
2484
2503
  data.baseUrl = baseUrl;
2504
+ // Reset the timer before the request goes out (same as index.jsx
2505
+ // handleSendMessage). Without this the new ThinkinHeaderInfo mounts while
2506
+ // the previous response's "completed" state is still in Redux and freezes
2507
+ // itself on that stale elapsed time.
2508
+ // streamStartTime stays null here: processStream sets it, so a question
2509
+ // has exactly one start time (two would make the new header look like a
2510
+ // superseded one and freeze it at 0m:00s).
2511
+ dispatch(smartBotActions.setThinkingContext({
2512
+ thinkingContent: "",
2513
+ thinkingHeaderMessage: "Working for 0m:00s",
2514
+ streamStartTime: null,
2515
+ isStreamCompleted: false,
2516
+ finalElapsedSeconds: null,
2517
+ }));
2485
2518
  prepareDataAndSendToAgent(data, false, {
2486
2519
  chatbotContext: chatbotContext,
2487
2520
  setChatbotContext: smartBotActions.setChatbotContext,
@@ -5117,17 +5150,26 @@ const TextContent = ({ bodyText, botData }) => {
5117
5150
  return (jsxRuntime.jsx("div", { children: renderTextContent() }));
5118
5151
  };
5119
5152
 
5120
- const ChipsContent = ({ bodyText, props }) => {
5153
+ const ChipsContent = ({ bodyText, props, botData, isFormDisabled = false }) => {
5121
5154
  const classes = useStyles$a();
5122
5155
  const globalClasses = globalStyles();
5123
- return (jsxRuntime.jsx("div", { className: `${globalClasses.flexRow} ${globalClasses.flexWrap} ${globalClasses.gap} ${globalClasses.verticalAlignCenter}`, children: bodyText.map((data, index) => {
5124
- const callBack = data.interactable && props
5125
- ? Object.entries(props).filter((entry) => entry[0] === data.actionName)?.[0]?.[1]
5126
- : null;
5127
- return (jsxRuntime.jsx(material.Typography, { component: "span", variant: "body1", className: `${classes.gptChips} ${data.interactable ? globalClasses.cursorPointer : ""}`, onClick: () => {
5128
- callBack && callBack(data);
5129
- }, children: data.displayText }, index));
5130
- }) }));
5156
+ // Suggested-question chips are one-shot: once the user has clicked one of them
5157
+ // or asked a new question the block disappears. isFormDisabled is true for
5158
+ // every bot message except the latest, so it flips exactly when the user
5159
+ // moves past these chips. Only chips that opt in via isQuestionsDisabled
5160
+ // behave this way; all other chips stay visible.
5161
+ if (botData?.isQuestionsDisabled && isFormDisabled) {
5162
+ return null;
5163
+ }
5164
+ const showHeaderTitle = !botData?.noShowHeaderTitle && Boolean(botData?.headerTitle?.length);
5165
+ return (jsxRuntime.jsxs(React.Fragment, { children: [showHeaderTitle && (jsxRuntime.jsx("div", { className: `${classes.chatbotText} ${classes.boldText} ${classes.combinedBlockHeaderTitle}`, children: botData.headerTitle })), jsxRuntime.jsx("div", { className: `${globalClasses.flexRow} ${globalClasses.flexWrap} ${globalClasses.gap} ${globalClasses.verticalAlignCenter}`, children: bodyText.map((data, index) => {
5166
+ const callBack = data.interactable && props
5167
+ ? Object.entries(props).filter((entry) => entry[0] === data.actionName)?.[0]?.[1]
5168
+ : null;
5169
+ return (jsxRuntime.jsx(material.Typography, { component: "span", variant: "body1", className: `${classes.gptChips} ${data.interactable ? globalClasses.cursorPointer : ""}`, onClick: () => {
5170
+ callBack && callBack(data);
5171
+ }, children: data.displayText }, index));
5172
+ }) })] }));
5131
5173
  };
5132
5174
 
5133
5175
  /**
@@ -6124,7 +6166,8 @@ const TableContent = ({ bodyText }) => {
6124
6166
  };
6125
6167
 
6126
6168
  const GraphContent = ({ bodyText }) => {
6127
- const { chartOptions } = bodyText || {};
6169
+ const { chartOptions, title } = bodyText || {};
6170
+ const classes = useStyles$a();
6128
6171
  const chartRef = React.useRef(null);
6129
6172
  const handleChartRef = (ref) => {
6130
6173
  chartRef.current = ref;
@@ -6135,7 +6178,7 @@ const GraphContent = ({ bodyText }) => {
6135
6178
  const options = {
6136
6179
  ...chartOptions,
6137
6180
  };
6138
- return (jsxRuntime.jsx(material.Grid, { container: true, children: jsxRuntime.jsx(material.Grid, { item: true, xs: 12, children: jsxRuntime.jsx(CoreChart, { options: options, handleChartRef: handleChartRef }) }) }));
6181
+ return (jsxRuntime.jsxs(material.Grid, { container: true, children: [Boolean(title?.length) && (jsxRuntime.jsx(material.Grid, { item: true, xs: 12, children: jsxRuntime.jsx("div", { className: `${classes.chatbotText} ${classes.boldText} ${classes.combinedBlockHeaderTitle}`, children: title }) })), jsxRuntime.jsx(material.Grid, { item: true, xs: 12, children: jsxRuntime.jsx(CoreChart, { options: options, handleChartRef: handleChartRef }) })] }));
6139
6182
  };
6140
6183
 
6141
6184
  const SelectableChips = ({ bodyText, chipType, props, utilityData }) => {
@@ -7523,7 +7566,6 @@ const STEP_FORM_TIMEOUT_KEY = "__stepFormTimedOut";
7523
7566
  */
7524
7567
  const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, showSavedFilters = true, preSelectedFilters = null, botProps = null, currentMode = "", agentId = "", sessionId = "", }) => {
7525
7568
  const dispatch = reactRedux.useDispatch();
7526
- const classes = useStyles$a();
7527
7569
  const savedFilterSets = reactRedux.useSelector((state) => state.smartBotReducer.savedFilterSets);
7528
7570
  const persistedFormValues = reactRedux.useSelector((state) => state.smartBotReducer.persistedFormValues);
7529
7571
  const chatbotContext = reactRedux.useSelector((state) => state.smartBotReducer.chatbotContext);
@@ -7796,13 +7838,13 @@ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, s
7796
7838
  case "text":
7797
7839
  return jsxRuntime.jsx(TextContent, { bodyText: parsedData.bodyText, botData: parsedData }, key);
7798
7840
  case "chips":
7799
- return parsedData.isMultiSelect ? (jsxRuntime.jsx(SelectableChips, { bodyText: parsedData.bodyText, chipType: "selectable", utilityData: parsedData.utilityData, props: botProps }, key)) : (jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: botProps }, key));
7841
+ return parsedData.isMultiSelect ? (jsxRuntime.jsx(SelectableChips, { bodyText: parsedData.bodyText, chipType: "selectable", utilityData: parsedData.utilityData, props: botProps }, key)) : (jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: botProps, botData: parsedData, isFormDisabled: isFormDisabled }, key));
7800
7842
  case "questions":
7801
7843
  return jsxRuntime.jsx(QuestionsContent, { bodyText: parsedData.bodyText, props: botProps }, key);
7802
7844
  case "html":
7803
7845
  return jsxRuntime.jsx(HtmlContent, { bodyText: parsedData.bodyText }, key);
7804
7846
  case "table":
7805
- return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, key);
7847
+ return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, parsedData.bodyText?.table_name || key);
7806
7848
  case "graph":
7807
7849
  return jsxRuntime.jsx(GraphContent, { bodyText: parsedData.bodyText }, key);
7808
7850
  case "slider":
@@ -7837,17 +7879,11 @@ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, s
7837
7879
  const body = renderItem(parsedData, index);
7838
7880
  if (!body)
7839
7881
  return;
7840
- // Skip the block header when it is just a repeat of the label the widget
7841
- // renders itself (parseResponse derives headerTitle from label for inputs).
7842
- const showHeaderTitle = !parsedData?.noShowHeaderTitle &&
7843
- Boolean(parsedData?.headerTitle?.length) &&
7844
- parsedData.headerTitle !== parsedData?.bodyText?.label;
7845
- const rendered = showHeaderTitle ? (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("div", { className: `${classes.chatbotText} ${classes.boldText} ${classes.combinedBlockHeaderTitle}`, children: parsedData.headerTitle }), body] }, `step-form-block-${index}`)) : (body);
7846
7882
  if (parsedData.bodyType === "button") {
7847
- buttonItems.push(rendered);
7883
+ buttonItems.push(body);
7848
7884
  }
7849
7885
  else {
7850
- formFields.push(rendered);
7886
+ formFields.push(body);
7851
7887
  }
7852
7888
  }
7853
7889
  catch (error) {
@@ -8357,7 +8393,10 @@ const renderWidgetItem = (item, index, isFormDisabled) => {
8357
8393
  case "text":
8358
8394
  return jsxRuntime.jsx(TextContent, { bodyText: parsedData.bodyText, botData: parsedData }, key);
8359
8395
  case "table":
8360
- return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, key);
8396
+ // Key on table_name so a different table never reuses a mounted grid.
8397
+ // ag-grid captures its fetch callback once at init, so a reused grid
8398
+ // would keep querying the previous table's table_name.
8399
+ return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, parsedData.bodyText?.table_name || key);
8361
8400
  case "graph":
8362
8401
  return jsxRuntime.jsx(GraphContent, { bodyText: parsedData.bodyText }, key);
8363
8402
  case "radio":
@@ -8695,7 +8734,7 @@ const StreamedContent = ({ botData, botProps }) => {
8695
8734
  const retryTimerRef = React.useRef(null); // Interval ref for countdown
8696
8735
  const retryCountdownRef = React.useRef(0); // Non-state countdown for interval callback
8697
8736
  const thinkingContentRef = React.useRef("");
8698
- reactRedux.useSelector((state) => {
8737
+ const thinkingContext = reactRedux.useSelector((state) => {
8699
8738
  return state.smartBotReducer.thinkingContext;
8700
8739
  });
8701
8740
  const stepRef = React.useRef(_isAbortedRemount
@@ -8718,7 +8757,10 @@ const StreamedContent = ({ botData, botProps }) => {
8718
8757
  const thinkingStartTimeRef = React.useRef(null);
8719
8758
  const thinkingTimeFinalRef = React.useRef(0);
8720
8759
  const thinkingHeaderMessageRef = React.useRef("Working for 0m:00s");
8721
- const streamStartTimeRef = React.useRef(null);
8760
+ // Inherit the in-flight start time so a remount (e.g. tab switch, where
8761
+ // processStream is not re-run) keeps the original one instead of resetting.
8762
+ // Null for a fresh question - processStream sets it.
8763
+ const streamStartTimeRef = React.useRef(thinkingContext?.isStreamCompleted ? null : thinkingContext?.streamStartTime || null);
8722
8764
  const thinkingDoneRef = React.useRef(false);
8723
8765
  const thinkingStartedRef = React.useRef(false);
8724
8766
  const setThinkingContentRef = React.useRef(setThinkingContent); // Store current setThinkingContent function
@@ -10303,7 +10345,6 @@ let instanceCounter = 0;
10303
10345
  let activeTabularInstanceId = null;
10304
10346
  const TabularContent = ({ steps: initialSteps, currentTabValue, children, questions: initialQuestions = [], questionsStepsMap: initialQuestionsStepsMap = {}, stepFormDataMap: initialStepFormDataMap = {}, isFormDisabled = false, sessionId: propSessionId = "", botProps = null, currentMode = "", agentId = "" }) => {
10305
10347
  const dispatch = reactRedux.useDispatch();
10306
- const chatClasses = useStyles$a();
10307
10348
  const stepFormStreamData = reactRedux.useSelector((state) => state.smartBotReducer.stepFormStreamData);
10308
10349
  const thinkingContext = reactRedux.useSelector((state) => state.smartBotReducer.thinkingContext);
10309
10350
  const streamStartTimeRef = React.useRef(thinkingContext?.streamStartTime);
@@ -10392,13 +10433,13 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
10392
10433
  case "text":
10393
10434
  return jsxRuntime.jsx(TextContent, { bodyText: parsedData.bodyText, botData: parsedData }, key);
10394
10435
  case "chips":
10395
- return parsedData.isMultiSelect ? (jsxRuntime.jsx(SelectableChips, { bodyText: parsedData.bodyText, chipType: "selectable", utilityData: parsedData.utilityData, props: botProps }, key)) : (jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: botProps }, key));
10436
+ return parsedData.isMultiSelect ? (jsxRuntime.jsx(SelectableChips, { bodyText: parsedData.bodyText, chipType: "selectable", utilityData: parsedData.utilityData, props: botProps }, key)) : (jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: botProps, botData: parsedData, isFormDisabled: isFormDisabled }, key));
10396
10437
  case "questions":
10397
10438
  return jsxRuntime.jsx(QuestionsContent, { bodyText: parsedData.bodyText, props: botProps }, key);
10398
10439
  case "image":
10399
10440
  return jsxRuntime.jsx(ImageContent, { bodyText: parsedData.bodyText }, key);
10400
10441
  case "table":
10401
- return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, key);
10442
+ return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, parsedData.bodyText?.table_name || key);
10402
10443
  case "graph":
10403
10444
  return jsxRuntime.jsx(GraphContent, { bodyText: parsedData.bodyText }, key);
10404
10445
  case "radio":
@@ -10429,13 +10470,7 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
10429
10470
  if (!parsedData)
10430
10471
  return null;
10431
10472
  const key = `restream-widget-${index}`;
10432
- const body = renderWidgetBody(parsedData, key);
10433
- if (!body)
10434
- return null;
10435
- const showHeaderTitle = !parsedData?.noShowHeaderTitle && Boolean(parsedData?.headerTitle?.length);
10436
- if (!showHeaderTitle)
10437
- return body;
10438
- return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx("div", { className: `${chatClasses.chatbotText} ${chatClasses.boldText} ${chatClasses.combinedBlockHeaderTitle}`, children: parsedData.headerTitle }), body] }, `restream-widget-block-${index}`));
10473
+ return renderWidgetBody(parsedData, key);
10439
10474
  }
10440
10475
  catch (e) {
10441
10476
  console.error("[TabularContent] renderWidget error:", e);
@@ -10799,19 +10834,10 @@ const resetActiveTabularInstance = () => {
10799
10834
  };
10800
10835
 
10801
10836
  const CombinedContent = ({ botData, props }) => {
10802
- const classes = useStyles$a();
10803
10837
  const isFormDisabled = botData?.isFormDisabled || false;
10804
10838
  const isTabEnabled = botData?.utilityData?.isTabEnabled;
10805
10839
  // Get the array of content items from bodyText
10806
10840
  const contentItems = Array.isArray(botData.bodyText) ? botData.bodyText : [];
10807
- // Renders the optional block-level header title above a content item.
10808
- const renderHeaderTitle = (parsedData) => {
10809
- const showHeaderTitle = !parsedData?.noShowHeaderTitle &&
10810
- Boolean(parsedData?.headerTitle?.length);
10811
- if (!showHeaderTitle)
10812
- return null;
10813
- return (jsxRuntime.jsx("div", { className: `${classes.chatbotText} ${classes.boldText} ${classes.combinedBlockHeaderTitle}`, children: parsedData.headerTitle }));
10814
- };
10815
10841
  // Function to render individual content based on its type
10816
10842
  const renderIndividualContent = (parsedData, index) => {
10817
10843
  const key = `combined-content-${index}`;
@@ -10823,12 +10849,12 @@ const CombinedContent = ({ botData, props }) => {
10823
10849
  return (jsxRuntime.jsx(SelectableChips, { bodyText: parsedData.bodyText, chipType: "selectable", utilityData: parsedData.utilityData, props: props }, key));
10824
10850
  }
10825
10851
  else {
10826
- return jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: props }, key);
10852
+ return (jsxRuntime.jsx(ChipsContent, { bodyText: parsedData.bodyText, props: props, botData: parsedData, isFormDisabled: isFormDisabled }, key));
10827
10853
  }
10828
10854
  case "questions":
10829
10855
  return jsxRuntime.jsx(QuestionsContent, { bodyText: parsedData.bodyText, props: props }, key);
10830
10856
  case "table":
10831
- return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, key);
10857
+ return jsxRuntime.jsx(TableContent, { bodyText: parsedData.bodyText }, parsedData.bodyText?.table_name || key);
10832
10858
  case "graph":
10833
10859
  return jsxRuntime.jsx(GraphContent, { bodyText: parsedData.bodyText }, key);
10834
10860
  case "slider":
@@ -10864,8 +10890,8 @@ const CombinedContent = ({ botData, props }) => {
10864
10890
  if (!parsedData) {
10865
10891
  return null;
10866
10892
  }
10867
- // Render the parsed content, preceded by the block's header title when present
10868
- return (jsxRuntime.jsxs(React.Fragment, { children: [renderHeaderTitle(parsedData), renderIndividualContent(parsedData, index)] }, `combined-block-${index}`));
10893
+ // Render the parsed content
10894
+ return renderIndividualContent(parsedData, index);
10869
10895
  }
10870
10896
  catch (error) {
10871
10897
  console.error(`Error parsing combined content item at index ${index}:`, error);
@@ -15878,10 +15904,14 @@ const SmartBot = (props) => {
15878
15904
  answerMode: answerMode,
15879
15905
  };
15880
15906
  // if(!isEmpty(userInput)) {
15907
+ // Clear the previous response's completed state so the incoming
15908
+ // ThinkinHeaderInfo doesn't freeze on it. streamStartTime stays null
15909
+ // here: processStream sets it, so a question has exactly one start
15910
+ // time (two would make the new header look like a superseded one).
15881
15911
  dispatch(smartBotActions.setThinkingContext({
15882
15912
  thinkingContent: "",
15883
15913
  thinkingHeaderMessage: "Working for 0m:00s",
15884
- streamStartTime: Date.now(),
15914
+ streamStartTime: null,
15885
15915
  isStreamCompleted: false,
15886
15916
  finalElapsedSeconds: null,
15887
15917
  }));