lisichatbot 1.2.4 → 1.2.6

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 +565 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisichatbot",
3
- "version": "1.2.4",
3
+ "version": "1.2.6",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "exports": {
package/src/index.js CHANGED
@@ -29,7 +29,15 @@ let elements = {
29
29
  let config = {
30
30
  selectedBackground: '#667eea',
31
31
  autoAdvanceDelay: 2000,
32
- enableAnimations: true
32
+ enableAnimations: true,
33
+ customRangeErrors: {
34
+ minRequired: 'Minimum value is required',
35
+ maxRequired: 'Maximum value is required',
36
+ bothRequired: 'Both minimum and maximum values are required',
37
+ minGreaterThanMax: 'Minimum must be less than maximum',
38
+ minBelowConstraint: 'Minimum must be at least {min}',
39
+ maxAboveConstraint: 'Maximum must be at most {max}'
40
+ }
33
41
  };
34
42
 
35
43
  let flowData = null;
@@ -137,7 +145,7 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
137
145
  console.log(` Calling editStep(${stepNumber})...`);
138
146
  editStep(stepNumber);
139
147
  };
140
- editIcon.style.setProperty('display', 'block', 'important');
148
+ editIcon.style.setProperty('display', 'flex', 'important');
141
149
  editIcon.style.setProperty('margin-left', '8px', 'important');
142
150
  editIcon.style.setProperty('cursor', 'pointer', 'important');
143
151
  console.log(` ✏️ Edit icon SHOWN (step ${stepNumber} is latest previous step with input)`);
@@ -176,7 +184,7 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
176
184
 
177
185
  // Only show if has input AND it's a previous step AND it's latest instance
178
186
  if (hasInput && stepNumber !== null && stepNumber < chatState.step && isLatest) {
179
- editIconAfterAppend.style.setProperty('display', 'block', 'important');
187
+ editIconAfterAppend.style.setProperty('display', 'flex', 'important');
180
188
 
181
189
  // Debug: Check spacing
182
190
  setTimeout(() => {
@@ -230,7 +238,7 @@ function updateEditIcons() {
230
238
  editStep(stepNumber);
231
239
  };
232
240
  editIcon.setAttribute('data-chat-step', stepNumber);
233
- editIcon.style.setProperty('display', 'block', 'important');
241
+ editIcon.style.setProperty('display', 'flex', 'important');
234
242
  editIcon.style.setProperty('cursor', 'pointer', 'important');
235
243
  editIcon.style.setProperty('margin-left', '8px', 'important');
236
244
  console.log(` ✏️ Step ${stepNumber}: Icon SHOWN (latest previous step)`);
@@ -483,6 +491,492 @@ function renderColorOptions(options, field) {
483
491
  });
484
492
  }
485
493
 
494
+ // =============================================================================
495
+ // SINGLE-SELECT-CUSTOM (WITH MIN/MAX RANGE)
496
+ // =============================================================================
497
+
498
+ function renderCustomSelectOptions(options, field, customConfig) {
499
+ if (!elements.messages) return;
500
+
501
+ // Find existing single-select option element
502
+ const optionSelector = '[data-chat-element="single-select-input"]';
503
+ const existingOption = document.querySelector(optionSelector);
504
+
505
+ if (!existingOption) {
506
+ console.error(`Element with ${optionSelector} not found in HTML.`);
507
+ return;
508
+ }
509
+
510
+ // Get existing data for pre-filling
511
+ const existingData = chatState.data[field];
512
+ console.log(`📝 Pre-filling ${field} (custom):`, existingData);
513
+
514
+ // Create wrapper to hold all options
515
+ const optionsWrapper = document.createElement('div');
516
+ optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
517
+
518
+ // Render regular options
519
+ options.forEach((option, index) => {
520
+ const optionName = option.name || option;
521
+ const optionValue = option.value !== undefined ? option.value : option;
522
+ const valueStr = typeof optionValue === 'object' ?
523
+ JSON.stringify(optionValue) : String(optionValue);
524
+
525
+ const clone = existingOption.cloneNode(true);
526
+ clone.style.display = '';
527
+
528
+ // Check if this option should be pre-selected
529
+ const shouldBeChecked = existingData !== undefined &&
530
+ !Array.isArray(existingData) && // Exclude array (custom range)
531
+ JSON.stringify(existingData) === JSON.stringify(optionValue);
532
+
533
+ if (shouldBeChecked) {
534
+ clone.classList.add('cf-checked');
535
+ clone.style.backgroundColor = config.selectedBackground;
536
+ console.log(` ✅ Pre-selected: ${optionName}`);
537
+ } else {
538
+ clone.classList.remove('cf-checked');
539
+ clone.style.backgroundColor = 'transparent';
540
+ }
541
+
542
+ clone.setAttribute('data-chat-element', 'single-select-input');
543
+ clone.setAttribute('data-field', field);
544
+ clone.setAttribute('data-value', valueStr);
545
+ clone.setAttribute('data-name', optionName);
546
+ clone.setAttribute('data-index', index);
547
+ clone.setAttribute('data-is-custom', 'false');
548
+
549
+ const input = clone.querySelector('[data-chat-input-element="input"]');
550
+ if (input) {
551
+ input.name = field;
552
+ input.value = valueStr;
553
+ input.checked = shouldBeChecked;
554
+ }
555
+
556
+ const tickIcon = clone.querySelector('[data-chat-input-element="tick-icon"]');
557
+ if (tickIcon) {
558
+ tickIcon.style.display = shouldBeChecked ? 'block' : 'none';
559
+ }
560
+
561
+ const textElement = clone.querySelector('[data-chat-input-element="text"]');
562
+ if (textElement) {
563
+ textElement.textContent = optionName;
564
+ }
565
+
566
+ optionsWrapper.appendChild(clone);
567
+ });
568
+
569
+ // Render custom option
570
+ if (customConfig) {
571
+ const customClone = existingOption.cloneNode(true);
572
+ customClone.style.display = '';
573
+
574
+ // Check if custom was selected before (data is array [min, max])
575
+ const isCustomSelected = existingData !== undefined &&
576
+ Array.isArray(existingData) &&
577
+ existingData.length === 2;
578
+
579
+ if (isCustomSelected) {
580
+ customClone.classList.add('cf-checked');
581
+ customClone.style.backgroundColor = config.selectedBackground;
582
+ console.log(` ✅ Pre-selected: Custom Range [${existingData[0]}, ${existingData[1]}]`);
583
+ } else {
584
+ customClone.classList.remove('cf-checked');
585
+ customClone.style.backgroundColor = 'transparent';
586
+ }
587
+
588
+ customClone.setAttribute('data-chat-element', 'single-select-input');
589
+ customClone.setAttribute('data-field', field);
590
+ customClone.setAttribute('data-value', customConfig.value || 'custom');
591
+ customClone.setAttribute('data-name', customConfig.name);
592
+ customClone.setAttribute('data-is-custom', 'true');
593
+
594
+ const customInput = customClone.querySelector('[data-chat-input-element="input"]');
595
+ if (customInput) {
596
+ customInput.name = field;
597
+ customInput.value = customConfig.value || 'custom';
598
+ customInput.checked = isCustomSelected;
599
+ }
600
+
601
+ const customTickIcon = customClone.querySelector('[data-chat-input-element="tick-icon"]');
602
+ if (customTickIcon) {
603
+ customTickIcon.style.display = isCustomSelected ? 'block' : 'none';
604
+ }
605
+
606
+ const customTextElement = customClone.querySelector('[data-chat-input-element="text"]');
607
+ if (customTextElement) {
608
+ customTextElement.textContent = customConfig.name;
609
+ }
610
+
611
+ optionsWrapper.appendChild(customClone);
612
+ }
613
+
614
+ // Append wrapper to messages
615
+ elements.messages.appendChild(optionsWrapper);
616
+
617
+ // Render min/max inputs
618
+ renderMinMaxInputs(field, customConfig, existingData);
619
+
620
+ scrollToBottom();
621
+
622
+ // Add click handlers for regular and custom options
623
+ const optionElements = optionsWrapper.querySelectorAll('[data-chat-input-element="container"]');
624
+ optionElements.forEach(el => {
625
+ el.onclick = (e) => {
626
+ e.stopPropagation();
627
+ e.preventDefault();
628
+ handleCustomSelectClick(el, field, customConfig);
629
+ };
630
+ });
631
+ }
632
+
633
+ function renderMinMaxInputs(field, customConfig, existingData) {
634
+ if (!elements.messages || !customConfig) return;
635
+
636
+ // Find min/max input templates
637
+ const minSelector = '[data-chat-element="single-select-custom-min"]';
638
+ const maxSelector = '[data-chat-element="single-select-custom-max"]';
639
+ const minTemplate = document.querySelector(minSelector);
640
+ const maxTemplate = document.querySelector(maxSelector);
641
+
642
+ if (!minTemplate || !maxTemplate) {
643
+ console.error('Min/Max input templates not found');
644
+ return;
645
+ }
646
+
647
+ // Create wrapper for min/max
648
+ const rangeWrapper = document.createElement('div');
649
+ rangeWrapper.setAttribute('data-chat-element', 'range-wrapper');
650
+ rangeWrapper.setAttribute('data-field', field);
651
+ rangeWrapper.style.marginBottom = '16px'; // Add space below wrapper
652
+
653
+ // Check if should show min/max (custom option selected)
654
+ // Data is stored as array [min, max]
655
+ const showMinMax = existingData !== undefined &&
656
+ Array.isArray(existingData) &&
657
+ existingData.length === 2;
658
+
659
+ rangeWrapper.style.display = showMinMax ? '' : 'none';
660
+
661
+ // Clone and setup min input
662
+ const minClone = minTemplate.cloneNode(true);
663
+ minClone.style.display = '';
664
+ minClone.style.marginRight = '12px'; // Add space between inputs
665
+ minClone.setAttribute('data-field', field);
666
+
667
+ const minInput = minClone.querySelector('[data-chat-input-element="input"]');
668
+ if (minInput) {
669
+ minInput.setAttribute('data-field', field);
670
+ minInput.setAttribute('data-input-type', 'min');
671
+ minInput.type = 'number';
672
+ minInput.min = customConfig.min !== undefined ? customConfig.min : 0;
673
+ minInput.max = customConfig.max !== undefined ? customConfig.max : 100;
674
+ minInput.value = showMinMax && existingData[0] !== undefined ? existingData[0] : '';
675
+ minInput.placeholder = `Min (${minInput.min}+)`;
676
+
677
+ if (showMinMax) {
678
+ console.log(` ✅ Pre-filled min: ${existingData[0]}`);
679
+ }
680
+ }
681
+
682
+ // Clone and setup max input
683
+ const maxClone = maxTemplate.cloneNode(true);
684
+ maxClone.style.display = '';
685
+ maxClone.setAttribute('data-field', field);
686
+
687
+ const maxInput = maxClone.querySelector('[data-chat-input-element="input"]');
688
+ if (maxInput) {
689
+ maxInput.setAttribute('data-field', field);
690
+ maxInput.setAttribute('data-input-type', 'max');
691
+ maxInput.type = 'number';
692
+ maxInput.min = customConfig.min !== undefined ? customConfig.min : 0;
693
+ maxInput.max = customConfig.max !== undefined ? customConfig.max : 100;
694
+ maxInput.value = showMinMax && existingData[1] !== undefined ? existingData[1] : '';
695
+ maxInput.placeholder = `Max (${maxInput.max} max)`;
696
+
697
+ if (showMinMax) {
698
+ console.log(` ✅ Pre-filled max: ${existingData[1]}`);
699
+ }
700
+ }
701
+
702
+ rangeWrapper.appendChild(minClone);
703
+ rangeWrapper.appendChild(maxClone);
704
+
705
+ // Add error message display
706
+ const errorDiv = document.createElement('div');
707
+ errorDiv.setAttribute('data-chat-element', 'range-error');
708
+ errorDiv.setAttribute('data-field', field);
709
+ errorDiv.style.color = '#ff4444';
710
+ errorDiv.style.fontSize = '14px';
711
+ errorDiv.style.marginTop = '8px';
712
+ errorDiv.style.display = 'none';
713
+ rangeWrapper.appendChild(errorDiv);
714
+
715
+ elements.messages.appendChild(rangeWrapper);
716
+
717
+ // Add handlers for both inputs
718
+ if (minInput && maxInput) {
719
+ const updateVisualFeedback = () => {
720
+ const minFilled = minInput.value.trim() !== '';
721
+ const maxFilled = maxInput.value.trim() !== '';
722
+ const anyFilled = minFilled || maxFilled;
723
+
724
+ // Get tick icons
725
+ const minTick = minClone.querySelector('[data-chat-input-element="tick-icon"]');
726
+ const maxTick = maxClone.querySelector('[data-chat-input-element="tick-icon"]');
727
+
728
+ // Update background and tick for BOTH inputs
729
+ if (anyFilled) {
730
+ minClone.style.backgroundColor = config.selectedBackground;
731
+ maxClone.style.backgroundColor = config.selectedBackground;
732
+ if (minTick) minTick.style.display = 'block';
733
+ if (maxTick) maxTick.style.display = 'block';
734
+ } else {
735
+ minClone.style.backgroundColor = 'transparent';
736
+ maxClone.style.backgroundColor = 'transparent';
737
+ if (minTick) minTick.style.display = 'none';
738
+ if (maxTick) maxTick.style.display = 'none';
739
+ }
740
+ };
741
+
742
+ minInput.onfocus = () => {
743
+ selectCustomOption(field);
744
+ updateVisualFeedback();
745
+ };
746
+ minInput.oninput = () => {
747
+ updateVisualFeedback();
748
+ validateMinMax(field, customConfig);
749
+ };
750
+
751
+ maxInput.onfocus = () => {
752
+ selectCustomOption(field);
753
+ updateVisualFeedback();
754
+ };
755
+ maxInput.oninput = () => {
756
+ updateVisualFeedback();
757
+ validateMinMax(field, customConfig);
758
+ };
759
+ }
760
+ }
761
+
762
+ function selectCustomOption(field) {
763
+ // Deselect all regular options
764
+ const allOptions = document.querySelectorAll(`[data-field="${field}"][data-chat-element="single-select-input"]`);
765
+ allOptions.forEach(opt => {
766
+ const isCustom = opt.getAttribute('data-is-custom') === 'true';
767
+ if (isCustom) {
768
+ // Select custom option
769
+ opt.classList.add('cf-checked');
770
+ opt.style.setProperty('background-color', config.selectedBackground, 'important');
771
+ const tickIcon = opt.querySelector('[data-chat-input-element="tick-icon"]');
772
+ if (tickIcon) tickIcon.style.setProperty('display', 'block', 'important');
773
+ } else {
774
+ // Deselect regular options
775
+ opt.classList.remove('cf-checked');
776
+ opt.style.setProperty('background-color', 'transparent', 'important');
777
+ const tickIcon = opt.querySelector('[data-chat-input-element="tick-icon"]');
778
+ if (tickIcon) tickIcon.style.setProperty('display', 'none', 'important');
779
+ }
780
+ });
781
+
782
+ // Show min/max inputs
783
+ const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
784
+ if (rangeWrapper) {
785
+ rangeWrapper.style.display = '';
786
+ }
787
+ }
788
+
789
+ function handleCustomSelectClick(element, field, customConfig) {
790
+ const isCustom = element.getAttribute('data-is-custom') === 'true';
791
+ const valueStr = element.getAttribute('data-value');
792
+ const optionName = element.getAttribute('data-name');
793
+
794
+ console.log(`Custom-select clicked:`, { field, name: optionName, isCustom });
795
+
796
+ // Deselect all options first
797
+ const allOptions = document.querySelectorAll(`[data-field="${field}"][data-chat-element="single-select-input"]`);
798
+ allOptions.forEach(opt => {
799
+ opt.classList.remove('cf-checked');
800
+ opt.style.setProperty('background-color', 'transparent', 'important');
801
+ const tickIcon = opt.querySelector('[data-chat-input-element="tick-icon"]');
802
+ if (tickIcon) tickIcon.style.setProperty('display', 'none', 'important');
803
+ });
804
+
805
+ // Select this option
806
+ element.classList.add('cf-checked');
807
+ element.style.setProperty('background-color', config.selectedBackground, 'important');
808
+ const tickIcon = element.querySelector('[data-chat-input-element="tick-icon"]');
809
+ if (tickIcon) tickIcon.style.setProperty('display', 'block', 'important');
810
+
811
+ if (isCustom) {
812
+ // Show min/max inputs
813
+ const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
814
+ if (rangeWrapper) {
815
+ rangeWrapper.style.display = '';
816
+ }
817
+
818
+ // Don't save data yet - wait for validation
819
+ chatState.currentSelection = null;
820
+ console.log(' ℹ️ Custom selected - waiting for min/max input');
821
+ } else {
822
+ // Hide min/max inputs and clear them
823
+ const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
824
+ if (rangeWrapper) {
825
+ rangeWrapper.style.display = 'none';
826
+
827
+ // Clear input values
828
+ const minInput = rangeWrapper.querySelector('[data-input-type="min"] [data-chat-input-element="input"]');
829
+ const maxInput = rangeWrapper.querySelector('[data-input-type="max"] [data-chat-input-element="input"]');
830
+
831
+ if (minInput) minInput.value = '';
832
+ if (maxInput) maxInput.value = '';
833
+
834
+ // Reset visual feedback (background and ticks)
835
+ const minContainer = rangeWrapper.querySelector('[data-chat-element="single-select-custom-min"]');
836
+ const maxContainer = rangeWrapper.querySelector('[data-chat-element="single-select-custom-max"]');
837
+
838
+ if (minContainer) {
839
+ minContainer.style.backgroundColor = 'transparent';
840
+ const minTick = minContainer.querySelector('[data-chat-input-element="tick-icon"]');
841
+ if (minTick) minTick.style.display = 'none';
842
+ }
843
+
844
+ if (maxContainer) {
845
+ maxContainer.style.backgroundColor = 'transparent';
846
+ const maxTick = maxContainer.querySelector('[data-chat-input-element="tick-icon"]');
847
+ if (maxTick) maxTick.style.display = 'none';
848
+ }
849
+
850
+ // Hide error message
851
+ const errorDiv = rangeWrapper.querySelector('[data-chat-element="range-error"]');
852
+ if (errorDiv) errorDiv.style.display = 'none';
853
+
854
+ console.log(' 🧹 Min/max inputs cleared and hidden');
855
+ }
856
+
857
+ // Parse and save regular value
858
+ let value;
859
+ try {
860
+ value = JSON.parse(valueStr);
861
+ } catch (e) {
862
+ value = valueStr;
863
+ }
864
+
865
+ chatState.data[field] = value;
866
+ chatState.currentSelection = { field, value, name: optionName };
867
+ console.log(' ✅ Regular option selected:', value);
868
+ enableNextButton();
869
+ }
870
+ }
871
+
872
+ function validateMinMax(field, customConfig) {
873
+ const minInput = document.querySelector(`[data-field="${field}"][data-input-type="min"] [data-chat-input-element="input"]`);
874
+ const maxInput = document.querySelector(`[data-field="${field}"][data-input-type="max"] [data-chat-input-element="input"]`);
875
+ const errorDiv = document.querySelector(`[data-chat-element="range-error"][data-field="${field}"]`);
876
+
877
+ if (!minInput || !maxInput) return { valid: false, error: null };
878
+
879
+ const minValue = parseFloat(minInput.value);
880
+ const maxValue = parseFloat(maxInput.value);
881
+ const minFilled = minInput.value.trim() !== '';
882
+ const maxFilled = maxInput.value.trim() !== '';
883
+
884
+ const minConstraint = customConfig.min !== undefined ? customConfig.min : 0;
885
+ const maxConstraint = customConfig.max !== undefined ? customConfig.max : 100;
886
+
887
+ // Helper to show error in div
888
+ const showErrorDiv = (message) => {
889
+ if (errorDiv) {
890
+ errorDiv.textContent = message;
891
+ errorDiv.style.display = 'block';
892
+ }
893
+ };
894
+
895
+ // Helper to hide error div
896
+ const hideErrorDiv = () => {
897
+ if (errorDiv) {
898
+ errorDiv.style.display = 'none';
899
+ }
900
+ };
901
+
902
+ // Validation rules - return error message if invalid
903
+
904
+ // 1. Check if only one is filled
905
+ if (minFilled && !maxFilled) {
906
+ const error = config.customRangeErrors.maxRequired;
907
+ showErrorDiv(error);
908
+ console.log(' ❌ Validation error:', error);
909
+ disableNextButton();
910
+ return { valid: false, error };
911
+ }
912
+
913
+ if (!minFilled && maxFilled) {
914
+ const error = config.customRangeErrors.minRequired;
915
+ showErrorDiv(error);
916
+ console.log(' ❌ Validation error:', error);
917
+ disableNextButton();
918
+ return { valid: false, error };
919
+ }
920
+
921
+ // 2. Check if both are empty
922
+ if (!minFilled && !maxFilled) {
923
+ hideErrorDiv();
924
+ disableNextButton();
925
+ return { valid: false, error: null };
926
+ }
927
+
928
+ // 3. Check if values are valid numbers
929
+ if (isNaN(minValue) || isNaN(maxValue)) {
930
+ const error = config.customRangeErrors.bothRequired;
931
+ showErrorDiv(error);
932
+ console.log(' ❌ Validation error:', error);
933
+ disableNextButton();
934
+ return { valid: false, error };
935
+ }
936
+
937
+ // 4. Check min constraint
938
+ if (minValue < minConstraint) {
939
+ const error = config.customRangeErrors.minBelowConstraint.replace('{min}', minConstraint);
940
+ showErrorDiv(error);
941
+ console.log(' ❌ Validation error:', error);
942
+ disableNextButton();
943
+ return { valid: false, error };
944
+ }
945
+
946
+ // 5. Check max constraint
947
+ if (maxValue > maxConstraint) {
948
+ const error = config.customRangeErrors.maxAboveConstraint.replace('{max}', maxConstraint);
949
+ showErrorDiv(error);
950
+ console.log(' ❌ Validation error:', error);
951
+ disableNextButton();
952
+ return { valid: false, error };
953
+ }
954
+
955
+ // 6. Check min < max
956
+ if (minValue >= maxValue) {
957
+ const error = config.customRangeErrors.minGreaterThanMax;
958
+ showErrorDiv(error);
959
+ console.log(' ❌ Validation error:', error);
960
+ disableNextButton();
961
+ return { valid: false, error };
962
+ }
963
+
964
+ // All valid! Hide error and save data as array
965
+ hideErrorDiv();
966
+
967
+ // Store as array [min, max]
968
+ chatState.data[field] = [minValue, maxValue];
969
+ chatState.currentSelection = {
970
+ field,
971
+ value: [minValue, maxValue],
972
+ name: `${minValue}-${maxValue}`
973
+ };
974
+
975
+ console.log(' ✅ Range valid - stored as array:', [minValue, maxValue]);
976
+ enableNextButton();
977
+ return { valid: true, error: null };
978
+ }
979
+
486
980
  // =============================================================================
487
981
  // TEXT/NUMBER INPUT RENDERING
488
982
  // =============================================================================
@@ -696,6 +1190,45 @@ async function handleNext() {
696
1190
  return;
697
1191
  }
698
1192
 
1193
+ // VALIDATION: Check for single-select-custom validation before proceeding
1194
+ if (currentStep.inputType === 'single-select-custom' && currentStep.input) {
1195
+ const field = currentStep.input.field;
1196
+ const customConfig = currentStep.input.custom;
1197
+
1198
+ // Check if custom range was selected (data is array)
1199
+ const selectedValue = chatState.data[field];
1200
+ const isCustomRange = Array.isArray(selectedValue);
1201
+
1202
+ if (isCustomRange && customConfig) {
1203
+ // Validate the custom range
1204
+ const validation = validateMinMax(field, customConfig);
1205
+
1206
+ if (!validation.valid && validation.error) {
1207
+ // Show error as bot message
1208
+ console.log(' ⚠️ Validation failed, showing error message');
1209
+
1210
+ // Hide current inputs (options and range wrapper)
1211
+ const optionsWrapper = document.querySelector('[data-chat-element="options-wrapper"]');
1212
+ if (optionsWrapper) {
1213
+ optionsWrapper.style.display = 'none';
1214
+ }
1215
+
1216
+ const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
1217
+ if (rangeWrapper) {
1218
+ rangeWrapper.style.display = 'none';
1219
+ }
1220
+
1221
+ // Add error message as bot message
1222
+ addMessage(validation.error, 'bot', false, null);
1223
+
1224
+ // Re-display the same step with fresh inputs
1225
+ await showNextStep();
1226
+
1227
+ return; // Stop here, don't proceed
1228
+ }
1229
+ }
1230
+ }
1231
+
699
1232
  // Call onNext validation if exists
700
1233
  if (currentStep.onNext) {
701
1234
  try {
@@ -745,6 +1278,13 @@ async function handleNext() {
745
1278
  chatState.step = targetStep;
746
1279
  console.log(` ✅ Jumped to step ${targetStep}`);
747
1280
 
1281
+ // Hide all range-wrappers
1282
+ const allRangeWrappers = document.querySelectorAll('[data-chat-element="range-wrapper"]');
1283
+ allRangeWrappers.forEach(wrapper => {
1284
+ wrapper.style.display = 'none';
1285
+ });
1286
+ console.log(' 🙈 Hidden all range-wrappers');
1287
+
748
1288
  // Update edit icons for the new position
749
1289
  updateEditIcons();
750
1290
 
@@ -759,6 +1299,13 @@ async function handleNext() {
759
1299
  // Normal flow: Move to next step
760
1300
  chatState.step++;
761
1301
 
1302
+ // Hide all range-wrappers (custom min/max inputs)
1303
+ const allRangeWrappers = document.querySelectorAll('[data-chat-element="range-wrapper"]');
1304
+ allRangeWrappers.forEach(wrapper => {
1305
+ wrapper.style.display = 'none';
1306
+ });
1307
+ console.log(' 🙈 Hidden all range-wrappers');
1308
+
762
1309
  // Update edit icons for all previous steps
763
1310
  updateEditIcons();
764
1311
 
@@ -828,6 +1375,20 @@ async function showNextStep() {
828
1375
  if (!inputRequired) {
829
1376
  enableNextButton();
830
1377
  }
1378
+ } else if (inputType === 'single-select-custom') {
1379
+ // Render single-select with custom min/max option
1380
+ renderCustomSelectOptions(
1381
+ nextStep.input.options,
1382
+ nextStep.input.field,
1383
+ nextStep.input.custom
1384
+ );
1385
+
1386
+ // Disable Next button initially (wait for selection/validation)
1387
+ if (inputRequired) {
1388
+ disableNextButton();
1389
+ } else {
1390
+ enableNextButton();
1391
+ }
831
1392
  } else {
832
1393
  // Render options (single-select or multi-select)
833
1394
  const isSingleSelect = inputType === 'single-select';