impact-chatbot 2.3.65 → 2.3.67

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
@@ -315,7 +315,7 @@ const useBotConfiguration = (setRefreshLoader, setEnableRefreshAction, dispatch)
315
315
  */
316
316
  const parseResponse = (data, type, agentId = "", currentMode = "", disableTimeAndName = false, sessionId = "", utilityData = {}, inputBody = null, utilityObject = {}) => {
317
317
  const timeString = disableTimeAndName ? "" : moment().format("DD-MM-YYYY HH:mm:ss");
318
- const userName = disableTimeAndName ? "" : "Alan";
318
+ const userName = disableTimeAndName ? "" : "Iris";
319
319
  switch (type || data?.type) {
320
320
  case "text":
321
321
  return {
@@ -471,7 +471,8 @@ const parseResponse = (data, type, agentId = "", currentMode = "", disableTimeAn
471
471
  minDate: data?.data?.minDate,
472
472
  maxDate: data?.data?.maxDate,
473
473
  isDisabled: data?.data?.isDisabled,
474
- paramName: data?.data?.param_name
474
+ paramName: data?.data?.param_name,
475
+ minStartDate: data?.data?.minStartDate
475
476
  }
476
477
  };
477
478
  case "dateRangePicker":
@@ -491,7 +492,8 @@ const parseResponse = (data, type, agentId = "", currentMode = "", disableTimeAn
491
492
  maxDate: data?.data?.maxDate,
492
493
  isDisabled: data?.data?.isDisabled,
493
494
  showMonthYearSelect: data?.data?.showMonthYearSelect,
494
- paramName: data?.data?.param_name
495
+ paramName: data?.data?.param_name,
496
+ minStartDate: data?.data?.minStartDate
495
497
  }
496
498
  };
497
499
  case "checkbox":
@@ -1392,7 +1394,7 @@ const useAgentFlow = (dateFormat, chatDataRef, currentMode, setShowChatPlacehold
1392
1394
  hasDirectActionType: false,
1393
1395
  };
1394
1396
  const loaderData = {
1395
- userName: "Alan",
1397
+ userName: "Iris",
1396
1398
  timeStamp: currentTimeString,
1397
1399
  userType: "loader",
1398
1400
  };
@@ -1958,7 +1960,7 @@ const useAgentFlow = (dateFormat, chatDataRef, currentMode, setShowChatPlacehold
1958
1960
  const failResponseText = {
1959
1961
  timeStamp: getCurrentDateTimeString(),
1960
1962
  userType: "bot",
1961
- userName: "Alan",
1963
+ userName: "Iris",
1962
1964
  bodyText: error?.response?.data?.data?.error?.response
1963
1965
  ? error?.response?.data?.data?.error?.response
1964
1966
  : "Sorry, chatbot is not available now",
@@ -2202,7 +2204,7 @@ const useChatFlow = (chatDataRef, setLoader, setFlowType, setScreenName, setUser
2202
2204
  return {
2203
2205
  timeStamp: timeString,
2204
2206
  userType: "bot",
2205
- userName: "Alan",
2207
+ userName: "Iris",
2206
2208
  headerTitle: headerTitle,
2207
2209
  bodyType: "chips",
2208
2210
  bodyText: screens.map((screen) => ({
@@ -2256,7 +2258,7 @@ const useChatFlow = (chatDataRef, setLoader, setFlowType, setScreenName, setUser
2256
2258
  flow_type: flowType,
2257
2259
  };
2258
2260
  const loaderData = {
2259
- userName: "Alan",
2261
+ userName: "Iris",
2260
2262
  timeStamp: currentTimeString,
2261
2263
  userType: "loader",
2262
2264
  };
@@ -2290,7 +2292,7 @@ const useChatFlow = (chatDataRef, setLoader, setFlowType, setScreenName, setUser
2290
2292
  const failResponseText = {
2291
2293
  timeStamp: getCurrentDateTimeString(),
2292
2294
  userType: "bot",
2293
- userName: "Alan",
2295
+ userName: "Iris",
2294
2296
  bodyText: options.data.data.message,
2295
2297
  enableLikes: false,
2296
2298
  headerTitle: "",
@@ -2309,7 +2311,7 @@ const useChatFlow = (chatDataRef, setLoader, setFlowType, setScreenName, setUser
2309
2311
  const failResponseText = {
2310
2312
  timeStamp: getCurrentDateTimeString(),
2311
2313
  userType: "bot",
2312
- userName: "Alan",
2314
+ userName: "Iris",
2313
2315
  bodyText: "Failed to fetch user screens. Please try again later.",
2314
2316
  headerTitle: "",
2315
2317
  enableLikes: false,
@@ -2381,7 +2383,7 @@ const useChatFlow = (chatDataRef, setLoader, setFlowType, setScreenName, setUser
2381
2383
  hasDirectActionType: fetchQuestions,
2382
2384
  };
2383
2385
  const loaderData = {
2384
- userName: "Alan",
2386
+ userName: "Iris",
2385
2387
  timeStamp: currentTimeString,
2386
2388
  userType: "loader",
2387
2389
  };
@@ -2501,7 +2503,7 @@ const useChatFlow = (chatDataRef, setLoader, setFlowType, setScreenName, setUser
2501
2503
  const failResponseText = {
2502
2504
  timeStamp: getCurrentDateTimeString(),
2503
2505
  userType: "bot",
2504
- userName: "Alan",
2506
+ userName: "Iris",
2505
2507
  bodyText: error?.response?.data?.data?.error?.response
2506
2508
  ? error?.response?.data?.data?.error?.response
2507
2509
  : "Sorry, chatbot is not available now",
@@ -2712,10 +2714,10 @@ const useChatSession = (chatDataRef, setFlowType, setScreenName, setUserInput, s
2712
2714
  {
2713
2715
  timeStamp: getCurrentDateTimeString(),
2714
2716
  userType: "bot",
2715
- userName: "Alan",
2717
+ userName: "Iris",
2716
2718
  headerTitle: `Hi ${getUserName()}!`,
2717
2719
  bodyType: "text",
2718
- bodyText: `I am Alan, your virtual assistant. I am here to help you on anything you are looking for in IA Smart Platform`,
2720
+ bodyText: `I am Iris, your virtual assistant. I am here to help you on anything you are looking for in IA Smart Platform`,
2719
2721
  },
2720
2722
  ];
2721
2723
  // chatDataRef.current[currentMode].conversations[
@@ -4881,7 +4883,7 @@ const ChatPlaceholder = (props) => {
4881
4883
  agentId: card.agentId || card.id
4882
4884
  }));
4883
4885
  const dataToMap = legacyAgentScreen || displayQuestions ? transformedCardList : rectangleData;
4884
- return (jsxs("div", { className: classes.placeholderContainer, children: [jsx("div", { className: classes.centerIconContainer, children: jsx(SvgCenter3D, { className: classes.centerIcon }) }), jsxs("div", { className: classes.headingContainer, children: [jsx(Typography, { variant: "h1", className: classes.heading, children: "Alan's Capabilities" }), jsx("span", { className: classes.alphaTag, children: "Beta" })] }), jsx(Typography, { variant: "body1", className: classes.headingHelperText, children: "Discover potential issues & opportunities Alan can help you with!" }), jsx("div", { className: classes.rectanglesContainer, children: dataToMap.map((item, index) => (jsx(Rectangle, { type: item.type, icon: item.icon, title: item.title, description: item.description, onClick: () => handleRectangleClick(item?.agentId, item?.title), hoverable: legacyAgentScreen || displayQuestions }, index))) })] }));
4886
+ return (jsxs("div", { className: classes.placeholderContainer, children: [jsx("div", { className: classes.centerIconContainer, children: jsx(SvgCenter3D, { className: classes.centerIcon }) }), jsxs("div", { className: classes.headingContainer, children: [jsx(Typography, { variant: "h1", className: classes.heading, children: "Iris's Capabilities" }), jsx("span", { className: classes.alphaTag, children: "Beta" })] }), jsx(Typography, { variant: "body1", className: classes.headingHelperText, children: "Discover potential issues & opportunities Iris can help you with!" }), jsx("div", { className: classes.rectanglesContainer, children: dataToMap.map((item, index) => (jsx(Rectangle, { type: item.type, icon: item.icon, title: item.title, description: item.description, onClick: () => handleRectangleClick(item?.agentId, item?.title), hoverable: legacyAgentScreen || displayQuestions }, index))) })] }));
4885
4887
  };
4886
4888
 
4887
4889
  const dateFormat = "DD-MM-YYYY HH:mm:ss";
@@ -5865,8 +5867,10 @@ const TableContent = ({ bodyText }) => {
5865
5867
  const onSelectionChanged = (event) => {
5866
5868
  try {
5867
5869
  const selectedList = event.api.getSelectedRows();
5868
- chatbotContext.tables = { ...chatbotContext?.tables, [table_name]: selectedList };
5869
- dispatch(setChatbotContext(chatbotContext));
5870
+ dispatch(setChatbotContext({
5871
+ ...chatbotContext,
5872
+ tables: { ...chatbotContext?.tables, [table_name]: selectedList },
5873
+ }));
5870
5874
  }
5871
5875
  catch (error) {
5872
5876
  console.error("Error in onSelectionChanged", error);
@@ -6709,12 +6713,14 @@ const SliderContent = ({ bodyText, isFormDisabled = false, messageIndex }) => {
6709
6713
  const handleChange = (value) => {
6710
6714
  try {
6711
6715
  setSliderValue(value?.target?.value);
6712
- chatbotContext[bodyText?.paramName] = {
6713
- ...chatbotContext?.[bodyText?.paramName],
6714
- [bodyText?.paramName]: value?.target?.value,
6715
- updated: true
6716
- };
6717
- dispatch(setChatbotContext(chatbotContext));
6716
+ dispatch(setChatbotContext({
6717
+ ...chatbotContext,
6718
+ [bodyText?.paramName]: {
6719
+ ...chatbotContext?.[bodyText?.paramName],
6720
+ [bodyText?.paramName]: value?.target?.value,
6721
+ updated: true,
6722
+ },
6723
+ }));
6718
6724
  dispatch(setPersistedFormValues({ [formKey]: value?.target?.value }));
6719
6725
  }
6720
6726
  catch (error) {
@@ -6761,17 +6767,20 @@ const SelectContent = ({ bodyText, isFormDisabled = false, messageIndex }) => {
6761
6767
  else {
6762
6768
  value = selectedOptions.value;
6763
6769
  }
6764
- chatbotContext[bodyText?.paramName] = {
6765
- ...chatbotContext?.[bodyText?.paramName],
6766
- [bodyText?.paramName]: value,
6767
- updated: true,
6770
+ const updatedContext = {
6771
+ ...chatbotContext,
6772
+ [bodyText?.paramName]: {
6773
+ ...chatbotContext?.[bodyText?.paramName],
6774
+ [bodyText?.paramName]: value,
6775
+ updated: true,
6776
+ },
6768
6777
  };
6769
6778
  // chatbotContext.select = {
6770
6779
  // ...chatbotContext?.select,
6771
6780
  // [bodyText?.paramName]: selectedOptions,
6772
6781
  // updated: true
6773
6782
  // };
6774
- dispatch(setChatbotContext(chatbotContext));
6783
+ dispatch(setChatbotContext(updatedContext));
6775
6784
  dispatch(setPersistedFormValues({ [formKey]: Array.isArray(selectedOptions) ? selectedOptions : [selectedOptions] }));
6776
6785
  // Notify cross-filter context if cascading is active
6777
6786
  if (isCascading) {
@@ -6811,12 +6820,14 @@ const SelectContent = ({ bodyText, isFormDisabled = false, messageIndex }) => {
6811
6820
  setCurrentSelectedOptions([]);
6812
6821
  setIsAllSelected(false);
6813
6822
  // Also clear from chatbotContext and persistedFormValues
6814
- chatbotContext[bodyText?.paramName] = {
6815
- ...chatbotContext?.[bodyText?.paramName],
6816
- [bodyText?.paramName]: [],
6817
- updated: true,
6818
- };
6819
- dispatch(setChatbotContext(chatbotContext));
6823
+ dispatch(setChatbotContext({
6824
+ ...chatbotContext,
6825
+ [bodyText?.paramName]: {
6826
+ ...chatbotContext?.[bodyText?.paramName],
6827
+ [bodyText?.paramName]: [],
6828
+ updated: true,
6829
+ },
6830
+ }));
6820
6831
  dispatch(setPersistedFormValues({ [formKey]: [] }));
6821
6832
  }
6822
6833
  }
@@ -6836,12 +6847,14 @@ const SelectContent = ({ bodyText, isFormDisabled = false, messageIndex }) => {
6836
6847
  isRequired: isRequired, isDisabled: isDisabled || isFormDisabled, isClearable: !(isDisabled || isFormDisabled), onClearAll: () => {
6837
6848
  setCurrentSelectedOptions([]);
6838
6849
  setIsAllSelected(false);
6839
- chatbotContext[bodyText?.paramName] = {
6840
- ...chatbotContext?.[bodyText?.paramName],
6841
- [bodyText?.paramName]: [],
6842
- updated: true,
6843
- };
6844
- dispatch(setChatbotContext(chatbotContext));
6850
+ dispatch(setChatbotContext({
6851
+ ...chatbotContext,
6852
+ [bodyText?.paramName]: {
6853
+ ...chatbotContext?.[bodyText?.paramName],
6854
+ [bodyText?.paramName]: [],
6855
+ updated: true,
6856
+ },
6857
+ }));
6845
6858
  dispatch(setPersistedFormValues({ [formKey]: [] }));
6846
6859
  // Notify cross-filter context to clear downstream filters
6847
6860
  if (isCascading) {
@@ -6892,12 +6905,14 @@ const SelectContent = ({ bodyText, isFormDisabled = false, messageIndex }) => {
6892
6905
  setCurrentSelectedOptions([...currentOptions]);
6893
6906
  setIsAllSelected(true);
6894
6907
  const allValues = allOptionsRef.current.map((opt) => opt.value);
6895
- chatbotContext[bodyText?.paramName] = {
6896
- ...chatbotContext?.[bodyText?.paramName],
6897
- [bodyText?.paramName]: allValues,
6898
- updated: true,
6899
- };
6900
- dispatch(setChatbotContext(chatbotContext));
6908
+ dispatch(setChatbotContext({
6909
+ ...chatbotContext,
6910
+ [bodyText?.paramName]: {
6911
+ ...chatbotContext?.[bodyText?.paramName],
6912
+ [bodyText?.paramName]: allValues,
6913
+ updated: true,
6914
+ },
6915
+ }));
6901
6916
  dispatch(setPersistedFormValues({ [formKey]: currentOptions }));
6902
6917
  // Notify cross-filter context if cascading is active
6903
6918
  if (isCascading) {
@@ -6929,12 +6944,14 @@ const DatePickerContent = ({ bodyText, isFormDisabled = false, messageIndex }) =
6929
6944
  const handleDateChange = (date) => {
6930
6945
  try {
6931
6946
  setSelectedDate(date);
6932
- chatbotContext[bodyText?.paramName] = {
6933
- ...chatbotContext?.[bodyText?.paramName],
6934
- [bodyText?.paramName]: moment(date).format(displayFormat),
6935
- updated: true
6936
- };
6937
- dispatch(setChatbotContext(chatbotContext));
6947
+ dispatch(setChatbotContext({
6948
+ ...chatbotContext,
6949
+ [bodyText?.paramName]: {
6950
+ ...chatbotContext?.[bodyText?.paramName],
6951
+ [bodyText?.paramName]: moment(date).format(displayFormat),
6952
+ updated: true,
6953
+ },
6954
+ }));
6938
6955
  dispatch(setPersistedFormValues({ [formKey]: date }));
6939
6956
  }
6940
6957
  catch (error) {
@@ -6968,15 +6985,17 @@ const DateRangePickerContent = ({ bodyText, isFormDisabled = false, messageIndex
6968
6985
  try {
6969
6986
  setStartDate(start);
6970
6987
  setEndDate(end);
6971
- chatbotContext[bodyText?.paramName] = {
6972
- ...chatbotContext?.[bodyText?.paramName],
6988
+ dispatch(setChatbotContext({
6989
+ ...chatbotContext,
6973
6990
  [bodyText?.paramName]: {
6974
- startDate: start ? moment(start).format(displayFormat) : null,
6975
- endDate: end ? moment(end).format(displayFormat) : null,
6991
+ ...chatbotContext?.[bodyText?.paramName],
6992
+ [bodyText?.paramName]: {
6993
+ startDate: start ? moment(start).format(displayFormat) : null,
6994
+ endDate: end ? moment(end).format(displayFormat) : null,
6995
+ },
6996
+ updated: true,
6976
6997
  },
6977
- updated: true,
6978
- };
6979
- dispatch(setChatbotContext(chatbotContext));
6998
+ }));
6980
6999
  dispatch(setPersistedFormValues({ [formKey]: { startDate: start, endDate: end } }));
6981
7000
  }
6982
7001
  catch (error) {
@@ -7017,12 +7036,14 @@ const CheckboxContent = ({ bodyText, isFormDisabled = false, messageIndex }) =>
7017
7036
  // [bodyText?.paramName]: isChecked?.currentTarget?.checked,
7018
7037
  // updated: true
7019
7038
  // };
7020
- chatbotContext[bodyText?.paramName] = {
7021
- ...chatbotContext?.[bodyText?.paramName],
7022
- [bodyText?.paramName]: isChecked?.currentTarget?.checked,
7023
- updated: true
7024
- };
7025
- dispatch(setChatbotContext(chatbotContext));
7039
+ dispatch(setChatbotContext({
7040
+ ...chatbotContext,
7041
+ [bodyText?.paramName]: {
7042
+ ...chatbotContext?.[bodyText?.paramName],
7043
+ [bodyText?.paramName]: isChecked?.currentTarget?.checked,
7044
+ updated: true,
7045
+ },
7046
+ }));
7026
7047
  dispatch(setPersistedFormValues({ [formKey]: isChecked?.currentTarget?.checked }));
7027
7048
  }
7028
7049
  catch (error) {
@@ -7051,12 +7072,14 @@ const RadioContent = ({ bodyText, isFormDisabled = false, messageIndex }) => {
7051
7072
  // [bodyText?.paramName]: value,
7052
7073
  // updated: true,
7053
7074
  // };
7054
- chatbotContext[bodyText?.paramName] = {
7055
- ...chatbotContext?.[bodyText?.paramName],
7056
- [bodyText?.paramName]: value,
7057
- updated: true
7058
- };
7059
- dispatch(setChatbotContext(chatbotContext));
7075
+ dispatch(setChatbotContext({
7076
+ ...chatbotContext,
7077
+ [bodyText?.paramName]: {
7078
+ ...chatbotContext?.[bodyText?.paramName],
7079
+ [bodyText?.paramName]: value,
7080
+ updated: true,
7081
+ },
7082
+ }));
7060
7083
  dispatch(setPersistedFormValues({ [formKey]: value }));
7061
7084
  }
7062
7085
  catch (error) {
@@ -7079,13 +7102,15 @@ const InputContent = ({ bodyText, isFormDisabled = false, messageIndex }) => {
7079
7102
  try {
7080
7103
  const newValue = event?.target?.value || event?.currentTarget?.value || "";
7081
7104
  setValue(newValue);
7082
- chatbotContext[bodyText?.paramName] = {
7083
- ...chatbotContext?.[bodyText?.paramName],
7084
- [bodyText?.paramName]: replaceSpecialCharToCharCode(newValue),
7085
- updated: true,
7086
- type: inputType,
7087
- };
7088
- dispatch(setChatbotContext(chatbotContext));
7105
+ dispatch(setChatbotContext({
7106
+ ...chatbotContext,
7107
+ [bodyText?.paramName]: {
7108
+ ...chatbotContext?.[bodyText?.paramName],
7109
+ [bodyText?.paramName]: replaceSpecialCharToCharCode(newValue),
7110
+ updated: true,
7111
+ type: inputType,
7112
+ },
7113
+ }));
7089
7114
  dispatch(setPersistedFormValues({ [formKey]: newValue }));
7090
7115
  }
7091
7116
  catch (error) {
@@ -7109,6 +7134,8 @@ const ImageContent = ({ bodyText }) => {
7109
7134
  } })), caption && (jsx("div", { children: jsx(TextRenderer, { text: caption }) }))] }));
7110
7135
  };
7111
7136
 
7137
+ // Key used to persist timeout state in sessionStorage (survives component remounts from tab switches)
7138
+ const STEP_FORM_TIMEOUT_KEY = "__stepFormTimedOut";
7112
7139
  /**
7113
7140
  * Renders form widget data inline within a step.
7114
7141
  * Reuses the same rendering logic as CombinedContent but designed
@@ -7127,16 +7154,49 @@ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, s
7127
7154
  const chatbotFilterOptions = useSelector((state) => state.smartBotReducer.chatbotFilterOptions);
7128
7155
  const dimensionHierarchies = useSelector((state) => state.userRoleManagementService?.dimensionHierarchies);
7129
7156
  const [isFormSubmitted, setIsFormSubmitted] = useState(false);
7157
+ const [isTimedOut, setIsTimedOut] = useState(() => sessionStorage.getItem(STEP_FORM_TIMEOUT_KEY) === "true");
7158
+ const timeoutRef = useRef(null);
7159
+ const prevIsFormDisabledRef = useRef(isFormDisabled);
7160
+ // 30-minute inactivity timeout: disable form and switch to agent_response tab
7161
+ const FORM_TIMEOUT_MS = 30 * 60 * 1000;
7162
+ useEffect(() => {
7163
+ if (isFormDisabled || isFormSubmitted || isTimedOut)
7164
+ return;
7165
+ const startTime = Date.now();
7166
+ timeoutRef.current = setTimeout(() => {
7167
+ setIsTimedOut(true);
7168
+ sessionStorage.setItem(STEP_FORM_TIMEOUT_KEY, "true");
7169
+ const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
7170
+ window.dispatchEvent(new CustomEvent("stepFormTimeout", { detail: { elapsedSeconds } }));
7171
+ }, FORM_TIMEOUT_MS);
7172
+ return () => {
7173
+ if (timeoutRef.current) {
7174
+ clearTimeout(timeoutRef.current);
7175
+ timeoutRef.current = null;
7176
+ }
7177
+ };
7178
+ }, [isFormDisabled, isFormSubmitted, isTimedOut]);
7179
+ // Clear timeout when form is submitted
7180
+ useEffect(() => {
7181
+ if (isFormSubmitted && timeoutRef.current) {
7182
+ clearTimeout(timeoutRef.current);
7183
+ timeoutRef.current = null;
7184
+ }
7185
+ }, [isFormSubmitted]);
7130
7186
  useEffect(() => {
7131
7187
  if (stepFormStreamData?.status === "streaming_start") {
7132
7188
  setIsFormSubmitted(true);
7133
7189
  }
7134
7190
  }, [stepFormStreamData]);
7135
- // Reset isFormSubmitted when parent signals the form should be enabled (new step_form from restream)
7191
+ // Reset isFormSubmitted and isTimedOut when parent signals the form should be enabled (new step_form from restream)
7192
+ // Only reset on a true→false TRANSITION (not on initial mount)
7136
7193
  useEffect(() => {
7137
- if (!isFormDisabled) {
7194
+ if (prevIsFormDisabledRef.current && !isFormDisabled) {
7138
7195
  setIsFormSubmitted(false);
7196
+ setIsTimedOut(false);
7197
+ sessionStorage.removeItem(STEP_FORM_TIMEOUT_KEY);
7139
7198
  }
7199
+ prevIsFormDisabledRef.current = isFormDisabled;
7140
7200
  }, [isFormDisabled]);
7141
7201
  const [isFilterSetOpen, setIsFilterSetOpen] = useState(false);
7142
7202
  const [selectedFilterSet, setSelectedFilterSet] = useState(() => {
@@ -7206,16 +7266,54 @@ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, s
7206
7266
  return val && (Array.isArray(val) ? val.length > 0 : !!val);
7207
7267
  });
7208
7268
  }, [persistedFormValues, messageIndex]);
7209
- const formFieldsDisabled = isFormDisabled || isFilterSelected || isFormSubmitted;
7210
- const savedFilterDisabled = isFormDisabled || isFormFieldUsed || isFormSubmitted;
7269
+ const formFieldsDisabled = isFormDisabled || isFilterSelected || isFormSubmitted || isTimedOut;
7270
+ const savedFilterDisabled = isFormDisabled || isFormFieldUsed || isFormSubmitted || isTimedOut;
7211
7271
  const requiredFieldsFilled = useMemo(() => {
7212
7272
  if (!formData || !Array.isArray(formData))
7213
7273
  return false;
7274
+ // Only validate fields that are both isRequired AND of a type that visually
7275
+ // shows the mandatory indicator (*) to the user (currently only "select" fields).
7214
7276
  const requiredParams = formData
7215
- .filter((item) => item?.data?.isRequired)
7277
+ .filter((item) => item?.data?.isRequired && item?.data?.is_mandatory)
7216
7278
  .map((item) => item.data.param_name);
7217
- if (requiredParams.length === 0)
7218
- return true;
7279
+ if (requiredParams.length === 0) {
7280
+ // Fallback: if no field has is_mandatory, check select-type fields with isRequired
7281
+ const selectRequiredParams = formData
7282
+ .filter((item) => item?.type === "select" && item?.data?.isRequired)
7283
+ .map((item) => item.data.param_name);
7284
+ if (selectRequiredParams.length === 0) {
7285
+ // No mandatory fields at all — require at least one field to be filled
7286
+ const allParams = formData
7287
+ .filter((item) => item?.data?.param_name && item?.type !== "button")
7288
+ .map((item) => item.data.param_name);
7289
+ return allParams.some((param) => {
7290
+ const ctx = chatbotContext?.[param];
7291
+ if (ctx && ctx.updated) {
7292
+ const val = ctx[param];
7293
+ if (val && (Array.isArray(val) ? val.length > 0 : !!val))
7294
+ return true;
7295
+ }
7296
+ const persistedKey = `${messageIndex}_${param}`;
7297
+ const persistedVal = persistedFormValues?.[persistedKey];
7298
+ if (persistedVal && (Array.isArray(persistedVal) ? persistedVal.length > 0 : !!persistedVal))
7299
+ return true;
7300
+ return false;
7301
+ });
7302
+ }
7303
+ return selectRequiredParams.every((param) => {
7304
+ const ctx = chatbotContext?.[param];
7305
+ if (ctx && ctx.updated) {
7306
+ const val = ctx[param];
7307
+ if (val && (Array.isArray(val) ? val.length > 0 : !!val))
7308
+ return true;
7309
+ }
7310
+ const persistedKey = `${messageIndex}_${param}`;
7311
+ const persistedVal = persistedFormValues?.[persistedKey];
7312
+ if (persistedVal && (Array.isArray(persistedVal) ? persistedVal.length > 0 : !!persistedVal))
7313
+ return true;
7314
+ return false;
7315
+ });
7316
+ }
7219
7317
  return requiredParams.every((param) => {
7220
7318
  // Check chatbotContext (populated by saved filters)
7221
7319
  const ctx = chatbotContext?.[param];
@@ -7330,7 +7428,7 @@ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, s
7330
7428
  case "radio":
7331
7429
  return jsx(RadioContent, { bodyText: parsedData.bodyText, isFormDisabled: formFieldsDisabled, messageIndex: messageIndex }, key);
7332
7430
  case "button":
7333
- return jsx(ButtonContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled || isFormSubmitted, isStepFormSubmit: true, isFormValid: requiredFieldsFilled }, key);
7431
+ return jsx(ButtonContent, { bodyText: parsedData.bodyText, isFormDisabled: isFormDisabled || isFormSubmitted || isTimedOut, isStepFormSubmit: true, isFormValid: requiredFieldsFilled }, key);
7334
7432
  case "input":
7335
7433
  return jsx(InputContent, { bodyText: parsedData.bodyText, isFormDisabled: formFieldsDisabled, messageIndex: messageIndex }, key);
7336
7434
  case "image":
@@ -7366,6 +7464,10 @@ const StepFormContent = ({ formData, messageIndex = 0, isFormDisabled = false, s
7366
7464
  ...(isFilterSelected && !isFormDisabled ? { pointerEvents: "none", opacity: 0.5 } : {}),
7367
7465
  }, children: crossFilterConfigs.length > 1 ? (jsx(CrossFilterProvider, { filters: crossFilterConfigs, fetchOptionsFn: fetchCrossFilterOptions, initialSelections: crossFilterInitialSelections, fetchOnMount: false, children: formFields })) : (formFields) }), buttonItems] }));
7368
7466
  };
7467
+ /** Reset the timeout flag (call when a new conversation/message starts) */
7468
+ const resetStepFormTimeoutFlag = () => {
7469
+ sessionStorage.removeItem(STEP_FORM_TIMEOUT_KEY);
7470
+ };
7369
7471
 
7370
7472
  const useStyles$4 = makeStyles$1((theme) => ({
7371
7473
  "@global": {
@@ -7795,7 +7897,42 @@ const AgentResponse$1 = (props) => {
7795
7897
 
7796
7898
  const StepsResponseTab = (props) => {
7797
7899
  const { steps, setSteps, stepsDone, setStepsDone, finalStepDone, setFinalStepDone, content, isStreaming, stepChange, currentMode, questions, questionsStepsMap, stepFormDataMap, isFormDisabled, streamingWidgetData, isRetrying, } = props;
7900
+ const dispatch = useDispatch();
7901
+ const thinkingContext = useSelector((state) => state.smartBotReducer.thinkingContext);
7902
+ const streamStartTimeRef = useRef(thinkingContext?.streamStartTime);
7903
+ // Keep streamStartTime ref in sync with Redux for use in event handler closures
7904
+ useEffect(() => {
7905
+ streamStartTimeRef.current = thinkingContext?.streamStartTime;
7906
+ }, [thinkingContext?.streamStartTime]);
7798
7907
  const [tabValue, setTabValue] = useState("steps");
7908
+ const [timeoutMessage, setTimeoutMessage] = useState("");
7909
+ const [isTimedOut, setIsTimedOut] = useState(false);
7910
+ // Switch to agent_response tab on form inactivity timeout (30 min)
7911
+ useEffect(() => {
7912
+ const handleStepFormTimeout = () => {
7913
+ setTimeoutMessage("This session timed out due to inactivity. Please start a new message to continue.");
7914
+ setIsTimedOut(true);
7915
+ setTabValue("agent_response");
7916
+ dispatch(setMinimizedStreamData(null));
7917
+ const elapsed = streamStartTimeRef.current
7918
+ ? Math.floor((Date.now() - streamStartTimeRef.current) / 1000)
7919
+ : null;
7920
+ const mins = elapsed != null ? Math.floor(elapsed / 60) : 0;
7921
+ const secs = elapsed != null ? elapsed % 60 : 0;
7922
+ const timedOutLabel = `Worked for ${mins}m:${String(secs).padStart(2, '0')}s`;
7923
+ dispatch(setThinkingContext({
7924
+ streamStartTime: null,
7925
+ isStreamCompleted: true,
7926
+ finalElapsedSeconds: elapsed,
7927
+ thinkingHeaderMessage: timedOutLabel,
7928
+ thinkingContent: "",
7929
+ }));
7930
+ };
7931
+ window.addEventListener("stepFormTimeout", handleStepFormTimeout);
7932
+ return () => {
7933
+ window.removeEventListener("stepFormTimeout", handleStepFormTimeout);
7934
+ };
7935
+ }, []);
7799
7936
  // When retry countdown starts, switch to agent_response to show it.
7800
7937
  // When retry actually fires (isRetrying goes false + isStreaming goes true), switch back to steps.
7801
7938
  useEffect(() => {
@@ -7823,8 +7960,8 @@ const StepsResponseTab = (props) => {
7823
7960
  icon: jsx(PsychologyOutlinedIcon, { fontSize: "large" }),
7824
7961
  },
7825
7962
  ], tabPanels: [
7826
- jsx(Steps$1, { steps: steps, setSteps: setSteps, done: stepsDone, setDone: setStepsDone, setTabValue: setTabValue, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, stepChange: stepChange, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: isFormDisabled }),
7827
- jsx(AgentResponse$1, { content: content, isStreaming: isStreaming, streamingWidgetData: streamingWidgetData }),
7963
+ jsx(Steps$1, { steps: steps, setSteps: setSteps, done: stepsDone, setDone: setStepsDone, setTabValue: setTabValue, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, stepChange: stepChange, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: isFormDisabled || isTimedOut }),
7964
+ jsxs(Fragment, { children: [jsx(AgentResponse$1, { content: content, isStreaming: isStreaming, streamingWidgetData: streamingWidgetData }), timeoutMessage && (jsx("div", { style: { marginTop: "8px", padding: "0 8px" }, children: jsx(TextRenderer, { text: timeoutMessage, thinking: "" }) }))] }),
7828
7965
  ], value: tabValue }) }));
7829
7966
  };
7830
7967
 
@@ -8268,6 +8405,8 @@ const StreamedContent = ({ botData }) => {
8268
8405
  ? botData?.utilityObject?.method
8269
8406
  : "PUT";
8270
8407
  delete botData.inputBody.chat_input;
8408
+ // Clear any stale timeout flag from a previous session
8409
+ resetStepFormTimeoutFlag();
8271
8410
  // Start the timer immediately when the API is called
8272
8411
  if (!streamStartTimeRef.current) {
8273
8412
  streamStartTimeRef.current = Date.now();
@@ -8652,6 +8791,7 @@ const StreamedContent = ({ botData }) => {
8652
8791
  // Check if this is a user-initiated abort (should not trigger retry)
8653
8792
  const isUserAbort = event.reason === "Network request aborted";
8654
8793
  event.statusCode === 503;
8794
+ const isAgentUnavailable = event.statusCode === 502 || event.statusCode === 404;
8655
8795
  if (isThinking) {
8656
8796
  setIsThinking(false);
8657
8797
  const endTime = Date.now();
@@ -8662,8 +8802,50 @@ const StreamedContent = ({ botData }) => {
8662
8802
  thinkingStartTimeRef.current = finalThinkingTime;
8663
8803
  messageToStoreRef.current.chatData.thinkingResponse.thinkingTime = finalThinkingTime;
8664
8804
  }
8805
+ // Agent unavailable (502/404) — show specific message, no retry
8806
+ if (isAgentUnavailable) {
8807
+ setWasStreamingAborted(true);
8808
+ wasStreamingAbortedRef.current = true;
8809
+ const currentSteps = stepRef.current;
8810
+ if (currentSteps.length > 0) {
8811
+ const lastStep = currentSteps[currentSteps.length - 1];
8812
+ if (lastStep.step_status === "not-completed") {
8813
+ lastStep.step_status = "completed";
8814
+ }
8815
+ stepRef.current = [...currentSteps];
8816
+ setSteps([...currentSteps]);
8817
+ }
8818
+ const agentUnavailableMessage = "Agent isn't available right now. Please contact your administrator or try again later.";
8819
+ const hasExistingWidgetsOnUnavail = messageToStoreRef.current.appendedData &&
8820
+ (isArray(messageToStoreRef.current.appendedData)
8821
+ ? messageToStoreRef.current.appendedData.length > 0
8822
+ : Object.keys(messageToStoreRef.current.appendedData).length > 0);
8823
+ if (hasExistingWidgetsOnUnavail) {
8824
+ const errorWidget = { type: "text", response: agentUnavailableMessage };
8825
+ if (isArray(messageToStoreRef.current.appendedData)) {
8826
+ messageToStoreRef.current.appendedData.push(errorWidget);
8827
+ }
8828
+ else {
8829
+ messageToStoreRef.current.appendedData = [messageToStoreRef.current.appendedData, errorWidget];
8830
+ }
8831
+ }
8832
+ else {
8833
+ messageToStoreRef.current.chatData.response = agentUnavailableMessage;
8834
+ }
8835
+ setContent(agentUnavailableMessage);
8836
+ dispatch(setMinimizedStreamData({
8837
+ isStreaming: false,
8838
+ stepHeader: questionsRef.current[questionsRef.current.length - 1] || "Agent unavailable",
8839
+ stepSubHeader: "Connection closed",
8840
+ stepStatus: "completed",
8841
+ streamStartTime: streamStartTimeRef.current,
8842
+ actionCount: questionsRef.current.length || 1,
8843
+ }));
8844
+ setStepsDone(true);
8845
+ setIsStreamingDone(true);
8846
+ }
8665
8847
  // Check if we can auto-retry (abrupt close or 503, but not user abort)
8666
- if (!isUserAbort && retryCountRef.current < MAX_RETRY_COUNT) {
8848
+ else if (!isUserAbort && retryCountRef.current < MAX_RETRY_COUNT) {
8667
8849
  // Still have retries left — start countdown instead of showing final error
8668
8850
  setStepsDone(true); // Switch to agent_response tab to show countdown
8669
8851
  const retryMessage = "The service is temporarily unavailable because of an unusual load. Please wait a moment and try again.";
@@ -8981,19 +9163,19 @@ const StreamedContent = ({ botData }) => {
8981
9163
  // [
8982
9164
  // {
8983
9165
  // header: "Finding relevant information",
8984
- // sub_header: "Alan is working to identify relevant information to user answer user query",
9166
+ // sub_header: "Iris is working to identify relevant information to user answer user query",
8985
9167
  // step_status: "not-completed",
8986
9168
  // },
8987
9169
  // ];
8988
9170
  // [
8989
9171
  // {
8990
9172
  // header: "Finding relevant information",
8991
- // sub_header: "Alan is working to identify relevant information to user answer user query",
9173
+ // sub_header: "Iris is working to identify relevant information to user answer user query",
8992
9174
  // step_status: "completed",
8993
9175
  // },
8994
9176
  // {
8995
9177
  // header: "Output Generation",
8996
- // sub_header: "Alan is finalizing response to user query",
9178
+ // sub_header: "Iris is finalizing response to user query",
8997
9179
  // step_status: "not-completed",
8998
9180
  // },
8999
9181
  // ];
@@ -9233,7 +9415,7 @@ const StreamedContent = ({ botData }) => {
9233
9415
  * @returns {JSX.Element} Rendered content with optional blinking cursor
9234
9416
  */
9235
9417
  const renderContent = () => {
9236
- return (jsxs("div", { className: classes.streamContainer, children: [jsx(StepsResponseTab, { steps: steps, stepChange: stepChange, setSteps: setSteps, stepsDone: stepsDone, setStepsDone: setStepsDone, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, content: content, isStreaming: isStreaming, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: botData?.isFormDisabled || false, streamingWidgetData: streamingWidgetData, isRetrying: isRetrying }), isRetrying && (jsx("div", { className: classes.retryContainer, children: jsxs("span", { className: classes.retryMessage, children: ["Auto re-trying in ", retryCountdown, "s"] }) }))] }));
9418
+ return (jsxs("div", { className: classes.streamContainer, children: [jsx(StepsResponseTab, { steps: steps, stepChange: stepChange, setSteps: setSteps, stepsDone: stepsDone, setStepsDone: setStepsDone, finalStepDone: finalStepDone, setFinalStepDone: setFinalStepDone, content: content, isStreaming: isStreaming, currentMode: currentMode, questions: questions, questionsStepsMap: questionsStepsMap, stepFormDataMap: stepFormDataMap, isFormDisabled: botData?.isFormDisabled || false, streamingWidgetData: streamingWidgetData, isRetrying: isRetrying }), isRetrying && (jsx("div", { className: classes.retryContainer, children: jsx(TextRenderer, { text: `Auto re-trying in ${retryCountdown}s`, thinking: "" }) }))] }));
9237
9419
  };
9238
9420
  if (currentMode === "agent") {
9239
9421
  return renderContent();
@@ -9504,6 +9686,12 @@ let activeTabularInstanceId = null;
9504
9686
  const TabularContent = ({ steps: initialSteps, currentTabValue, children, questions: initialQuestions = [], questionsStepsMap: initialQuestionsStepsMap = {}, stepFormDataMap: initialStepFormDataMap = {}, isFormDisabled = false, sessionId: propSessionId = "" }) => {
9505
9687
  const dispatch = useDispatch();
9506
9688
  const stepFormStreamData = useSelector((state) => state.smartBotReducer.stepFormStreamData);
9689
+ const thinkingContext = useSelector((state) => state.smartBotReducer.thinkingContext);
9690
+ const streamStartTimeRef = useRef(thinkingContext?.streamStartTime);
9691
+ // Keep streamStartTime ref in sync with Redux for use in event handler closures
9692
+ useEffect(() => {
9693
+ streamStartTimeRef.current = thinkingContext?.streamStartTime;
9694
+ }, [thinkingContext?.streamStartTime]);
9507
9695
  const [tabValue, setTabValue] = useState(currentTabValue);
9508
9696
  // Local state — initialized from props, updated by SSE chunks
9509
9697
  const [stepsState, setStepsState] = useState(() => {
@@ -9526,6 +9714,8 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
9526
9714
  const [hasNewStepFormFromRestream, setHasNewStepFormFromRestream] = useState(false);
9527
9715
  const [activeFormIntent, setActiveFormIntent] = useState(null);
9528
9716
  const [retryCountdown, setRetryCountdown] = useState(0);
9717
+ const [timeoutMessage, setTimeoutMessage] = useState("");
9718
+ const [isTimedOut, setIsTimedOut] = useState(false);
9529
9719
  // Stable unique instance ID for this TabularContent mount
9530
9720
  const instanceIdRef = useRef(null);
9531
9721
  if (instanceIdRef.current === null) {
@@ -9550,6 +9740,33 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
9550
9740
  window.removeEventListener("stepFormStreamCompleted", handleStepFormCompleted);
9551
9741
  };
9552
9742
  }, []);
9743
+ // Switch to agent_response tab on form inactivity timeout (30 min)
9744
+ useEffect(() => {
9745
+ const handleStepFormTimeout = () => {
9746
+ setTimeoutMessage("This session timed out due to inactivity. Please start a new message to continue.");
9747
+ setStepFormSubmitted(true);
9748
+ setIsTimedOut(true);
9749
+ setTabValue("agent_response");
9750
+ dispatch(setMinimizedStreamData(null));
9751
+ const elapsed = streamStartTimeRef.current
9752
+ ? Math.floor((Date.now() - streamStartTimeRef.current) / 1000)
9753
+ : null;
9754
+ const mins = elapsed != null ? Math.floor(elapsed / 60) : 0;
9755
+ const secs = elapsed != null ? elapsed % 60 : 0;
9756
+ const timedOutLabel = `Worked for ${mins}m:${String(secs).padStart(2, '0')}s`;
9757
+ dispatch(setThinkingContext({
9758
+ streamStartTime: null,
9759
+ isStreamCompleted: true,
9760
+ finalElapsedSeconds: elapsed,
9761
+ thinkingHeaderMessage: timedOutLabel,
9762
+ thinkingContent: "",
9763
+ }));
9764
+ };
9765
+ window.addEventListener("stepFormTimeout", handleStepFormTimeout);
9766
+ return () => {
9767
+ window.removeEventListener("stepFormTimeout", handleStepFormTimeout);
9768
+ };
9769
+ }, []);
9553
9770
  // Render a widget item from SSE data
9554
9771
  const renderWidget = (item, index) => {
9555
9772
  try {
@@ -9888,8 +10105,8 @@ const TabularContent = ({ steps: initialSteps, currentTabValue, children, questi
9888
10105
  icon: jsx(PsychologyOutlinedIcon, { fontSize: "large" }),
9889
10106
  },
9890
10107
  ], tabPanels: [
9891
- jsx(Steps, { steps: stepsState, questions: questionsState, questionsStepsMap: questionsStepsMapState, stepFormDataMap: stepFormDataMapState, isFormDisabled: (isFormDisabled && !isRestreaming) || stepFormSubmitted, activeFormIntent: hasNewStepFormFromRestream ? activeFormIntent : null, isRestreaming: isRestreaming }),
9892
- jsxs(AgentResponse, { children: [children, renderedWidgets.length > 0 && (jsx("div", { className: "restream-widget-content", children: renderedWidgets })), retryCountdown > 0 && (jsx("div", { style: { marginTop: "8px" }, children: jsxs("span", { style: { fontFamily: "Manrope", fontSize: "13px", fontWeight: 500, color: "#374151" }, children: ["Auto re-trying in ", retryCountdown, "s"] }) }))] }),
10108
+ jsx(Steps, { steps: stepsState, questions: questionsState, questionsStepsMap: questionsStepsMapState, stepFormDataMap: stepFormDataMapState, isFormDisabled: (isFormDisabled && !isRestreaming) || stepFormSubmitted || isTimedOut, activeFormIntent: hasNewStepFormFromRestream ? activeFormIntent : null, isRestreaming: isRestreaming }),
10109
+ jsxs(AgentResponse, { children: [children, renderedWidgets.length > 0 && (jsx("div", { className: "restream-widget-content", children: renderedWidgets })), retryCountdown > 0 && (jsx("div", { style: { marginTop: "8px" }, children: jsx(TextRenderer, { text: `Auto re-trying in ${retryCountdown}s`, thinking: "" }) })), timeoutMessage && (jsx("div", { style: { marginTop: "8px" }, children: jsx(TextRenderer, { text: timeoutMessage, thinking: "" }) }))] }),
9893
10110
  ], value: tabValue }) }));
9894
10111
  };
9895
10112
  /** Reset the active instance tracker (call when a new conversation starts from the input field) */
@@ -12939,7 +13156,7 @@ let singleConversationNewData = [
12939
13156
  {
12940
13157
  header: "Creating Plan of Action",
12941
13158
  step_status: "completed",
12942
- sub_header: "Alan is working to determine the best approach to answer user query",
13159
+ sub_header: "Iris is working to determine the best approach to answer user query",
12943
13160
  current_intent: "List the products which are about to change status in next 10 days"
12944
13161
  },
12945
13162
  {
@@ -14863,7 +15080,7 @@ const SmartBot = (props) => {
14863
15080
  try {
14864
15081
  // if (localStorage.getItem("isStreaming") === "true") {
14865
15082
  // displaySnackMessages(
14866
- // "Alan is processing request in another tab, This request can be processed once Alan is available",
15083
+ // "Iris is processing request in another tab, This request can be processed once Iris is available",
14867
15084
  // "warning"
14868
15085
  // );
14869
15086
  // return;
@@ -15205,7 +15422,7 @@ const SmartBot = (props) => {
15205
15422
  :
15206
15423
  jsx(ChatbotSaveFilterComponent$1, { savedFilterSets: savedFilterSets, partialClose: partialClose, chatBotWidth: chatBotWidth }), inputText: userInput, threadList: ["Home"], hideMenuArrow: hideMenu, newChatScreen: newChatScreen, isModuleListLoading: modulesLoading, suggestionBanner: {
15207
15424
  freeTextHeading: "Try adding more details :",
15208
- freeTextContent: "Alan works better when you provide more context and pointed questions",
15425
+ freeTextContent: "Iris works better when you provide more context and pointed questions",
15209
15426
  }, isStopIcon: isStop, onStopIconClick: onStopIconClick, footerText: "AI-generated responses may contain errors\u2014please verify important information", showSuggestionBanner: showSavedFilters ? false : showSuggestionBanner, onCloseSuggestionBanner: () => {
15210
15427
  setShowSuggestionBanner(false);
15211
15428
  }, landingScreen: isLandingScreen, onLikeClick: (params, params2) => {