lisichatbot 1.3.9 → 1.4.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +195 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisichatbot",
3
- "version": "1.3.9",
3
+ "version": "1.4.1",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "exports": {
package/src/index.js CHANGED
@@ -540,6 +540,8 @@ function renderOptions(options, field, isSingleSelect = true) {
540
540
  optionsWrapper.style.alignItems = 'flex-start';
541
541
  optionsWrapper.style.gap = '8px';
542
542
 
543
+ let hasPreselectedOptions = false; // ✅ Track if any options are pre-selected
544
+
543
545
  options.forEach((option, index) => {
544
546
  const optionName = option.name || option;
545
547
  const optionValue = option.value !== undefined ? option.value : option;
@@ -562,6 +564,7 @@ function renderOptions(options, field, isSingleSelect = true) {
562
564
  clone.classList.add('cf-checked');
563
565
  clone.style.backgroundColor = config.selectedBackground;
564
566
  console.log(` ✅ Pre-selected: ${optionName}`);
567
+ hasPreselectedOptions = true; // ✅ Mark that we have pre-selections
565
568
  } else {
566
569
  clone.classList.remove('cf-checked');
567
570
  clone.style.backgroundColor = 'transparent';
@@ -610,6 +613,31 @@ function renderOptions(options, field, isSingleSelect = true) {
610
613
  handleOptionClick(el, field, isSingleSelect);
611
614
  };
612
615
  });
616
+
617
+ // ✅ FIX: Enable Next button if options are pre-selected
618
+ if (hasPreselectedOptions) {
619
+ // Set currentSelection for pre-selected options
620
+ if (isSingleSelect) {
621
+ chatState.currentSelection = {
622
+ field,
623
+ value: existingData,
624
+ name: options.find(o => JSON.stringify(o.value !== undefined ? o.value : o) === JSON.stringify(existingData))?.name || existingData
625
+ };
626
+ } else {
627
+ const selectedNames = existingData.map(val => {
628
+ const opt = options.find(o => JSON.stringify(o.value !== undefined ? o.value : o) === JSON.stringify(val));
629
+ return opt ? (opt.name || opt) : val;
630
+ }).join(', ');
631
+ chatState.currentSelection = {
632
+ field,
633
+ value: existingData,
634
+ name: selectedNames
635
+ };
636
+ }
637
+
638
+ enableNextButton();
639
+ console.log(` ✅ Pre-selected options found - Next button enabled`);
640
+ }
613
641
  }
614
642
 
615
643
  // =============================================================================
@@ -637,6 +665,8 @@ function renderColorOptions(options, field) {
637
665
  optionsWrapper.style.alignItems = 'flex-start';
638
666
  optionsWrapper.style.gap = '8px';
639
667
 
668
+ let hasPreselectedOptions = false; // ✅ Track if any options are pre-selected
669
+
640
670
  options.forEach((option, index) => {
641
671
  const optionName = option.name || option;
642
672
  const optionValue = option.value !== undefined ? option.value : option;
@@ -654,6 +684,7 @@ function renderColorOptions(options, field) {
654
684
  clone.classList.add('cf-checked');
655
685
  clone.style.backgroundColor = config.selectedBackground;
656
686
  console.log(` ✅ Pre-selected color: ${optionName}`);
687
+ hasPreselectedOptions = true; // ✅ Mark that we have pre-selections
657
688
  } else {
658
689
  clone.classList.remove('cf-checked');
659
690
  clone.style.backgroundColor = 'transparent';
@@ -711,6 +742,24 @@ function renderColorOptions(options, field) {
711
742
  handleOptionClick(el, field, false);
712
743
  };
713
744
  });
745
+
746
+ // ✅ FIX: Enable Next button if options are pre-selected
747
+ if (hasPreselectedOptions) {
748
+ // Set currentSelection for pre-selected options
749
+ const selectedNames = existingData.map(val => {
750
+ const opt = options.find(o => JSON.stringify(o.value !== undefined ? o.value : o) === JSON.stringify(val));
751
+ return opt ? (opt.name || opt) : val;
752
+ }).join(', ');
753
+
754
+ chatState.currentSelection = {
755
+ field,
756
+ value: existingData,
757
+ name: selectedNames
758
+ };
759
+
760
+ enableNextButton();
761
+ console.log(` ✅ Pre-selected color options found - Next button enabled`);
762
+ }
714
763
  }
715
764
 
716
765
  // =============================================================================
@@ -738,6 +787,8 @@ function renderCustomSelectOptions(options, field, customConfig) {
738
787
  optionsWrapper.style.alignItems = 'flex-start';
739
788
  optionsWrapper.style.gap = '8px';
740
789
 
790
+ let hasPreselectedOption = false; // ✅ Track if any option is pre-selected
791
+
741
792
  options.forEach((option, index) => {
742
793
  const optionName = option.name || option;
743
794
  const optionValue = option.value !== undefined ? option.value : option;
@@ -755,6 +806,7 @@ function renderCustomSelectOptions(options, field, customConfig) {
755
806
  clone.classList.add('cf-checked');
756
807
  clone.style.backgroundColor = config.selectedBackground;
757
808
  console.log(` ✅ Pre-selected: ${optionName}`);
809
+ hasPreselectedOption = true; // ✅ Mark that we have a pre-selection
758
810
  } else {
759
811
  clone.classList.remove('cf-checked');
760
812
  clone.style.backgroundColor = 'transparent';
@@ -799,6 +851,7 @@ function renderCustomSelectOptions(options, field, customConfig) {
799
851
  customClone.classList.add('cf-checked');
800
852
  customClone.style.backgroundColor = config.selectedBackground;
801
853
  console.log(` ✅ Pre-selected: Custom Range [${existingData[0]}, ${existingData[1]}]`);
854
+ hasPreselectedOption = true; // ✅ Mark that custom is pre-selected
802
855
  } else {
803
856
  customClone.classList.remove('cf-checked');
804
857
  customClone.style.backgroundColor = 'transparent';
@@ -842,6 +895,35 @@ function renderCustomSelectOptions(options, field, customConfig) {
842
895
  handleCustomSelectClick(el, field, customConfig);
843
896
  };
844
897
  });
898
+
899
+ // ✅ FIX: Enable Next button if option is pre-selected
900
+ if (hasPreselectedOption) {
901
+ // Set currentSelection
902
+ const isCustomSelected = Array.isArray(existingData) && existingData.length === 2;
903
+
904
+ if (isCustomSelected) {
905
+ // Custom range selected - currentSelection will be set by validateMinMax
906
+ const validation = validateMinMax(field, customConfig);
907
+ if (validation.valid) {
908
+ enableNextButton();
909
+ console.log(` ✅ Pre-selected custom range is valid - Next button enabled`);
910
+ }
911
+ } else {
912
+ // Regular option selected
913
+ const selectedOption = options.find(o =>
914
+ JSON.stringify(o.value !== undefined ? o.value : o) === JSON.stringify(existingData)
915
+ );
916
+
917
+ chatState.currentSelection = {
918
+ field,
919
+ value: existingData,
920
+ name: selectedOption ? (selectedOption.name || selectedOption) : existingData
921
+ };
922
+
923
+ enableNextButton();
924
+ console.log(` ✅ Pre-selected option found - Next button enabled`);
925
+ }
926
+ }
845
927
  }
846
928
 
847
929
  function renderMinMaxInputs(field, customConfig, existingData) {
@@ -1331,10 +1413,21 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
1331
1413
  inputElement.type = inputType === 'number' ? 'number' : 'text';
1332
1414
 
1333
1415
  inputElement.oninput = (e) => {
1334
- const value = inputType === 'number' ? parseFloat(e.target.value) : e.target.value;
1416
+ const rawValue = e.target.value;
1417
+ const value = inputType === 'number' ? parseFloat(rawValue) : rawValue;
1335
1418
 
1336
- chatState.data[field] = value;
1337
- chatState.currentSelection = { field, value, name: value.toString() };
1419
+ // ✅ FIX: Only set data and currentSelection if value is valid
1420
+ const isEmpty = rawValue === '' || (inputType === 'number' && isNaN(value));
1421
+ const isEmptyText = inputType === 'text' && (!rawValue || rawValue.trim() === '');
1422
+
1423
+ if (!isEmpty && !isEmptyText) {
1424
+ chatState.data[field] = value;
1425
+ chatState.currentSelection = { field, value, name: value.toString() };
1426
+ } else {
1427
+ // Clear data and selection for empty values
1428
+ delete chatState.data[field];
1429
+ chatState.currentSelection = null;
1430
+ }
1338
1431
 
1339
1432
  if (hasValidation) {
1340
1433
  const min = inputConfig.min;
@@ -1343,7 +1436,7 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
1343
1436
  let isValid = true;
1344
1437
  let errorMessage = '';
1345
1438
 
1346
- if (e.target.value === '' || (inputType === 'number' && isNaN(value))) {
1439
+ if (rawValue === '' || (inputType === 'number' && isNaN(value))) {
1347
1440
  isValid = false;
1348
1441
  errorMessage = 'Please enter a valid number';
1349
1442
  } else if (min !== undefined && value < min) {
@@ -1362,21 +1455,43 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
1362
1455
  console.log(` ❌ Number input invalid: ${errorMessage} - Next button disabled`);
1363
1456
  }
1364
1457
  } else {
1365
- // ✅ FIX: For text inputs without validation, enable button as user types
1366
- if (e.target.value && e.target.value.trim() !== '') {
1367
- enableNextButton();
1368
- console.log(` ✅ Text input has value - Next button enabled`);
1369
- } else {
1370
- // Check if input is required
1371
- const currentStep = flowData.flow[chatState.step];
1372
- const inputRequired = currentStep.inputRequired === true;
1373
-
1374
- if (inputRequired) {
1375
- disableNextButton();
1376
- console.log(` 🔒 Text input empty and required - Next button disabled`);
1458
+ // ✅ FIX: Handle number inputs without validation
1459
+ if (inputType === 'number') {
1460
+ // For number inputs, check if value is valid
1461
+ if (rawValue === '' || isNaN(value)) {
1462
+ // Empty or NaN
1463
+ const currentStep = flowData.flow[chatState.step];
1464
+ const inputRequired = currentStep.inputRequired === true;
1465
+
1466
+ if (inputRequired) {
1467
+ disableNextButton();
1468
+ console.log(` 🔒 Number input empty/NaN and required - Next button disabled`);
1469
+ } else {
1470
+ enableNextButton();
1471
+ console.log(` 🔓 Number input empty/NaN but not required - Next button enabled`);
1472
+ }
1377
1473
  } else {
1474
+ // Has valid number value
1378
1475
  enableNextButton();
1379
- console.log(` 🔓 Text input empty but not required - Next button enabled`);
1476
+ console.log(` Number input has value: ${value} - Next button enabled`);
1477
+ }
1478
+ } else {
1479
+ // Text input logic
1480
+ if (rawValue && rawValue.trim() !== '') {
1481
+ enableNextButton();
1482
+ console.log(` ✅ Text input has value - Next button enabled`);
1483
+ } else {
1484
+ // ✅ Check if input is required
1485
+ const currentStep = flowData.flow[chatState.step];
1486
+ const inputRequired = currentStep.inputRequired === true;
1487
+
1488
+ if (inputRequired) {
1489
+ disableNextButton();
1490
+ console.log(` 🔒 Text input empty and required - Next button disabled`);
1491
+ } else {
1492
+ enableNextButton();
1493
+ console.log(` 🔓 Text input empty but not required - Next button enabled`);
1494
+ }
1380
1495
  }
1381
1496
  }
1382
1497
  }
@@ -1516,6 +1631,33 @@ async function handleNext() {
1516
1631
  console.log('Selection required but none made');
1517
1632
  return;
1518
1633
  }
1634
+
1635
+ // ✅ FIX: Validate text/number inputs when required
1636
+ if (currentStep.input && inputRequired) {
1637
+ const inputType = currentStep.inputType || 'single-select';
1638
+
1639
+ if (inputType === 'text' || inputType === 'number') {
1640
+ const field = currentStep.input.field;
1641
+ const value = chatState.data[field];
1642
+
1643
+ // Check for empty or invalid values
1644
+ if (inputType === 'text') {
1645
+ if (value === undefined || value === null || value === '' || (typeof value === 'string' && value.trim() === '')) {
1646
+ console.log('❌ Text input required but empty - cannot proceed');
1647
+ disableNextButton();
1648
+ return;
1649
+ }
1650
+ } else if (inputType === 'number') {
1651
+ if (value === undefined || value === null || value === '' || isNaN(value)) {
1652
+ console.log('❌ Number input required but empty/NaN - cannot proceed');
1653
+ disableNextButton();
1654
+ return;
1655
+ }
1656
+ }
1657
+
1658
+ console.log(`✅ ${inputType} input validation passed:`, value);
1659
+ }
1660
+ }
1519
1661
 
1520
1662
  if (currentStep.inputType === 'single-select-custom' && currentStep.input) {
1521
1663
  const field = currentStep.input.field;
@@ -1704,9 +1846,19 @@ async function showNextStep() {
1704
1846
  } else if (inputType === 'multi-select-color') {
1705
1847
  renderColorOptions(nextStep.input.options, nextStep.input.field);
1706
1848
 
1707
- if (!inputRequired) {
1849
+ // ✅ Check if pre-filled - renderColorOptions handles enabling if pre-filled
1850
+ // Only disable if NOT pre-filled and required
1851
+ const existingData = chatState.data[nextStep.input.field];
1852
+ const hasPreFill = Array.isArray(existingData) && existingData.length > 0;
1853
+
1854
+ if (inputRequired && !hasPreFill) {
1855
+ disableNextButton();
1856
+ console.log(' 🔒 Multi-select-color required with no pre-fill - Next button disabled');
1857
+ } else if (!inputRequired && !hasPreFill) {
1708
1858
  enableNextButton();
1859
+ console.log(' 🔓 Multi-select-color not required - Next button enabled');
1709
1860
  }
1861
+ // If hasPreFill, renderColorOptions already handled it
1710
1862
  } else if (inputType === 'single-select-custom') {
1711
1863
  renderCustomSelectOptions(
1712
1864
  nextStep.input.options,
@@ -1714,11 +1866,18 @@ async function showNextStep() {
1714
1866
  nextStep.input.custom
1715
1867
  );
1716
1868
 
1717
- if (inputRequired) {
1869
+ // ✅ Check if pre-filled - renderCustomSelectOptions handles enabling if pre-filled
1870
+ const existingData = chatState.data[nextStep.input.field];
1871
+ const hasPreFill = existingData !== undefined && existingData !== null;
1872
+
1873
+ if (inputRequired && !hasPreFill) {
1718
1874
  disableNextButton();
1719
- } else {
1875
+ console.log(' 🔒 Single-select-custom required with no pre-fill - Next button disabled');
1876
+ } else if (!inputRequired && !hasPreFill) {
1720
1877
  enableNextButton();
1878
+ console.log(' 🔓 Single-select-custom not required - Next button enabled');
1721
1879
  }
1880
+ // If hasPreFill, renderCustomSelectOptions already handled it
1722
1881
  } else if (inputType === 'multi-select-dropdown') {
1723
1882
  // ✅ Render multi-select dropdown
1724
1883
  renderMultiSelectDropdown(nextStep.input.options, nextStep.input.field);
@@ -1735,9 +1894,24 @@ async function showNextStep() {
1735
1894
  const isSingleSelect = inputType === 'single-select';
1736
1895
  renderOptions(nextStep.input.options, nextStep.input.field, isSingleSelect);
1737
1896
 
1738
- if (!inputRequired) {
1897
+ // ✅ Check if pre-filled - renderOptions handles enabling if pre-filled
1898
+ const existingData = chatState.data[nextStep.input.field];
1899
+ let hasPreFill = false;
1900
+
1901
+ if (isSingleSelect) {
1902
+ hasPreFill = existingData !== undefined && existingData !== null;
1903
+ } else {
1904
+ hasPreFill = Array.isArray(existingData) && existingData.length > 0;
1905
+ }
1906
+
1907
+ if (inputRequired && !hasPreFill) {
1908
+ disableNextButton();
1909
+ console.log(` 🔒 ${inputType} required with no pre-fill - Next button disabled`);
1910
+ } else if (!inputRequired && !hasPreFill) {
1739
1911
  enableNextButton();
1912
+ console.log(` 🔓 ${inputType} not required - Next button enabled`);
1740
1913
  }
1914
+ // If hasPreFill, renderOptions already handled it
1741
1915
  }
1742
1916
  } else {
1743
1917
  const delay = nextStep.autoAdvanceDelay !== undefined ? nextStep.autoAdvanceDelay : config.autoAdvanceDelay;