lisichatbot 1.2.3 → 1.2.5

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 +592 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisichatbot",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
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;
@@ -267,6 +275,10 @@ function renderOptions(options, field, isSingleSelect = true) {
267
275
  return;
268
276
  }
269
277
 
278
+ // Get existing data for this field (for pre-filling when editing)
279
+ const existingData = chatState.data[field];
280
+ console.log(`📝 Pre-filling ${field}:`, existingData);
281
+
270
282
  // Create wrapper to hold all options
271
283
  const optionsWrapper = document.createElement('div');
272
284
  optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
@@ -284,9 +296,27 @@ function renderOptions(options, field, isSingleSelect = true) {
284
296
  // Make clone visible (remove display:none from template)
285
297
  clone.style.display = '';
286
298
 
287
- // Reset to unchecked state
288
- clone.classList.remove('cf-checked');
289
- clone.style.backgroundColor = 'transparent';
299
+ // Check if this option should be pre-selected
300
+ let shouldBeChecked = false;
301
+ if (isSingleSelect) {
302
+ // For single-select, check if value matches existing data
303
+ shouldBeChecked = existingData !== undefined &&
304
+ JSON.stringify(existingData) === JSON.stringify(optionValue);
305
+ } else {
306
+ // For multi-select, check if value is in array
307
+ shouldBeChecked = Array.isArray(existingData) &&
308
+ existingData.some(v => JSON.stringify(v) === JSON.stringify(optionValue));
309
+ }
310
+
311
+ // Set checked state
312
+ if (shouldBeChecked) {
313
+ clone.classList.add('cf-checked');
314
+ clone.style.backgroundColor = config.selectedBackground;
315
+ console.log(` ✅ Pre-selected: ${optionName}`);
316
+ } else {
317
+ clone.classList.remove('cf-checked');
318
+ clone.style.backgroundColor = 'transparent';
319
+ }
290
320
 
291
321
  // Set data attributes on container
292
322
  clone.setAttribute('data-chat-element', inputTypeAttr);
@@ -301,14 +331,14 @@ function renderOptions(options, field, isSingleSelect = true) {
301
331
  input.setAttribute('data-chat-element', inputTypeAttr);
302
332
  input.name = field;
303
333
  input.value = valueStr;
304
- input.checked = false; // Ensure unchecked initially
334
+ input.checked = shouldBeChecked; // Pre-check if needed
305
335
  input.onclick = (e) => e.stopPropagation(); // Prevent click bubbling
306
336
  }
307
337
 
308
- // Find and ensure tick icon is hidden initially
338
+ // Find and set tick icon visibility
309
339
  const tickIcon = clone.querySelector('[data-chat-input-element="tick-icon"]');
310
340
  if (tickIcon) {
311
- tickIcon.style.display = 'none';
341
+ tickIcon.style.display = shouldBeChecked ? 'block' : 'none';
312
342
  }
313
343
 
314
344
  // Find and set text
@@ -343,6 +373,526 @@ function renderOptions(options, field, isSingleSelect = true) {
343
373
  });
344
374
  }
345
375
 
376
+ // =============================================================================
377
+ // MULTI-SELECT-COLOR OPTIONS RENDERING
378
+ // =============================================================================
379
+
380
+ function renderColorOptions(options, field) {
381
+ if (!elements.messages) return;
382
+
383
+ // Find existing color option element in HTML by data-chat-element
384
+ const optionSelector = '[data-chat-element="multi-select-color"]';
385
+ const existingOption = document.querySelector(optionSelector);
386
+
387
+ if (!existingOption) {
388
+ console.error(`Element with ${optionSelector} not found in HTML. Please add it to your HTML.`);
389
+ return;
390
+ }
391
+
392
+ // Get existing data for this field (for pre-filling when editing)
393
+ const existingData = chatState.data[field];
394
+ console.log(`📝 Pre-filling ${field} (color):`, existingData);
395
+
396
+ // Create wrapper to hold all options
397
+ const optionsWrapper = document.createElement('div');
398
+ optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
399
+
400
+ // Clone and fill option element for each option
401
+ options.forEach((option, index) => {
402
+ const optionName = option.name || option;
403
+ const optionValue = option.value !== undefined ? option.value : option;
404
+ const optionColor = option.color || '#cccccc'; // Default gray if no color
405
+ const valueStr = typeof optionValue === 'object' ?
406
+ JSON.stringify(optionValue) : String(optionValue);
407
+
408
+ // Clone existing option element
409
+ const clone = existingOption.cloneNode(true);
410
+
411
+ // Make clone visible (remove display:none from template)
412
+ clone.style.display = '';
413
+
414
+ // Check if this option should be pre-selected (multi-select behavior)
415
+ const shouldBeChecked = Array.isArray(existingData) &&
416
+ existingData.some(v => JSON.stringify(v) === JSON.stringify(optionValue));
417
+
418
+ // Set checked state
419
+ if (shouldBeChecked) {
420
+ clone.classList.add('cf-checked');
421
+ clone.style.backgroundColor = config.selectedBackground;
422
+ console.log(` ✅ Pre-selected color: ${optionName}`);
423
+ } else {
424
+ clone.classList.remove('cf-checked');
425
+ clone.style.backgroundColor = 'transparent';
426
+ }
427
+
428
+ // Set data attributes on container
429
+ clone.setAttribute('data-chat-element', 'multi-select-color');
430
+ clone.setAttribute('data-field', field);
431
+ clone.setAttribute('data-value', valueStr);
432
+ clone.setAttribute('data-name', optionName);
433
+ clone.setAttribute('data-index', index);
434
+ clone.setAttribute('data-color', optionColor);
435
+
436
+ // Find and set input
437
+ const input = clone.querySelector('[data-chat-input-element="input"]');
438
+ if (input) {
439
+ input.setAttribute('data-chat-element', 'multi-select-color');
440
+ input.name = field;
441
+ input.value = valueStr;
442
+ input.checked = shouldBeChecked; // Pre-check if needed
443
+ input.onclick = (e) => e.stopPropagation(); // Prevent click bubbling
444
+ }
445
+
446
+ // Find and set tick icon visibility
447
+ const tickIcon = clone.querySelector('[data-chat-input-element="tick-icon"]');
448
+ if (tickIcon) {
449
+ tickIcon.style.display = shouldBeChecked ? 'block' : 'none';
450
+ }
451
+
452
+ // Find and set color block
453
+ const colorBlock = clone.querySelector('[data-chat-input-element="color-block"]');
454
+ if (colorBlock) {
455
+ colorBlock.style.backgroundColor = optionColor;
456
+ colorBlock.style.display = ''; // Make sure it's visible
457
+ } else {
458
+ console.warn('Color block element not found in multi-select-color template');
459
+ }
460
+
461
+ // Find and set text
462
+ const textElement = clone.querySelector('[data-chat-input-element="text"]');
463
+ if (textElement) {
464
+ textElement.textContent = optionName;
465
+ textElement.style.display = ''; // Make sure text is visible
466
+ } else {
467
+ console.error('Text element not found in option');
468
+ }
469
+
470
+ // Append to wrapper
471
+ optionsWrapper.appendChild(clone);
472
+ });
473
+
474
+ // Append wrapper to messages
475
+ elements.messages.appendChild(optionsWrapper);
476
+ scrollToBottom();
477
+
478
+ // Add click handlers with proper event handling
479
+ const optionElements = optionsWrapper.querySelectorAll('[data-chat-input-element="container"]');
480
+ optionElements.forEach(el => {
481
+ // Clear any existing onclick
482
+ el.onclick = null;
483
+
484
+ // Add new click handler with stopPropagation
485
+ el.onclick = (e) => {
486
+ e.stopPropagation(); // Prevent bubbling
487
+ e.preventDefault(); // Prevent default behavior
488
+ // Use multi-select behavior (isSingleSelect = false)
489
+ handleOptionClick(el, field, false);
490
+ };
491
+ });
492
+ }
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
+
652
+ // Check if should show min/max (custom option selected)
653
+ // Data is stored as array [min, max]
654
+ const showMinMax = existingData !== undefined &&
655
+ Array.isArray(existingData) &&
656
+ existingData.length === 2;
657
+
658
+ rangeWrapper.style.display = showMinMax ? '' : 'none';
659
+
660
+ // Clone and setup min input
661
+ const minClone = minTemplate.cloneNode(true);
662
+ minClone.style.display = '';
663
+ minClone.setAttribute('data-field', field);
664
+
665
+ const minInput = minClone.querySelector('[data-chat-input-element="input"]');
666
+ if (minInput) {
667
+ minInput.setAttribute('data-field', field);
668
+ minInput.setAttribute('data-input-type', 'min');
669
+ minInput.type = 'number';
670
+ minInput.min = customConfig.min !== undefined ? customConfig.min : 0;
671
+ minInput.max = customConfig.max !== undefined ? customConfig.max : 100;
672
+ minInput.value = showMinMax && existingData[0] !== undefined ? existingData[0] : '';
673
+ minInput.placeholder = `Min (${minInput.min}+)`;
674
+
675
+ if (showMinMax) {
676
+ console.log(` ✅ Pre-filled min: ${existingData[0]}`);
677
+ }
678
+ }
679
+
680
+ // Clone and setup max input
681
+ const maxClone = maxTemplate.cloneNode(true);
682
+ maxClone.style.display = '';
683
+ maxClone.setAttribute('data-field', field);
684
+
685
+ const maxInput = maxClone.querySelector('[data-chat-input-element="input"]');
686
+ if (maxInput) {
687
+ maxInput.setAttribute('data-field', field);
688
+ maxInput.setAttribute('data-input-type', 'max');
689
+ maxInput.type = 'number';
690
+ maxInput.min = customConfig.min !== undefined ? customConfig.min : 0;
691
+ maxInput.max = customConfig.max !== undefined ? customConfig.max : 100;
692
+ maxInput.value = showMinMax && existingData[1] !== undefined ? existingData[1] : '';
693
+ maxInput.placeholder = `Max (${maxInput.max} max)`;
694
+
695
+ if (showMinMax) {
696
+ console.log(` ✅ Pre-filled max: ${existingData[1]}`);
697
+ }
698
+ }
699
+
700
+ rangeWrapper.appendChild(minClone);
701
+ rangeWrapper.appendChild(maxClone);
702
+
703
+ // Add error message display
704
+ const errorDiv = document.createElement('div');
705
+ errorDiv.setAttribute('data-chat-element', 'range-error');
706
+ errorDiv.setAttribute('data-field', field);
707
+ errorDiv.style.color = '#ff4444';
708
+ errorDiv.style.fontSize = '14px';
709
+ errorDiv.style.marginTop = '8px';
710
+ errorDiv.style.display = 'none';
711
+ rangeWrapper.appendChild(errorDiv);
712
+
713
+ elements.messages.appendChild(rangeWrapper);
714
+
715
+ // Add focus handlers to select custom option when editing
716
+ if (minInput) {
717
+ minInput.onfocus = () => selectCustomOption(field);
718
+ minInput.oninput = () => validateMinMax(field, customConfig);
719
+ }
720
+ if (maxInput) {
721
+ maxInput.onfocus = () => selectCustomOption(field);
722
+ maxInput.oninput = () => validateMinMax(field, customConfig);
723
+ }
724
+ }
725
+
726
+ function selectCustomOption(field) {
727
+ // Deselect all regular options
728
+ const allOptions = document.querySelectorAll(`[data-field="${field}"][data-chat-element="single-select-input"]`);
729
+ allOptions.forEach(opt => {
730
+ const isCustom = opt.getAttribute('data-is-custom') === 'true';
731
+ if (isCustom) {
732
+ // Select custom option
733
+ opt.classList.add('cf-checked');
734
+ opt.style.setProperty('background-color', config.selectedBackground, 'important');
735
+ const tickIcon = opt.querySelector('[data-chat-input-element="tick-icon"]');
736
+ if (tickIcon) tickIcon.style.setProperty('display', 'block', 'important');
737
+ } else {
738
+ // Deselect regular options
739
+ opt.classList.remove('cf-checked');
740
+ opt.style.setProperty('background-color', 'transparent', 'important');
741
+ const tickIcon = opt.querySelector('[data-chat-input-element="tick-icon"]');
742
+ if (tickIcon) tickIcon.style.setProperty('display', 'none', 'important');
743
+ }
744
+ });
745
+
746
+ // Show min/max inputs
747
+ const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
748
+ if (rangeWrapper) {
749
+ rangeWrapper.style.display = '';
750
+ }
751
+ }
752
+
753
+ function handleCustomSelectClick(element, field, customConfig) {
754
+ const isCustom = element.getAttribute('data-is-custom') === 'true';
755
+ const valueStr = element.getAttribute('data-value');
756
+ const optionName = element.getAttribute('data-name');
757
+
758
+ console.log(`Custom-select clicked:`, { field, name: optionName, isCustom });
759
+
760
+ // Deselect all options first
761
+ const allOptions = document.querySelectorAll(`[data-field="${field}"][data-chat-element="single-select-input"]`);
762
+ allOptions.forEach(opt => {
763
+ opt.classList.remove('cf-checked');
764
+ opt.style.setProperty('background-color', 'transparent', 'important');
765
+ const tickIcon = opt.querySelector('[data-chat-input-element="tick-icon"]');
766
+ if (tickIcon) tickIcon.style.setProperty('display', 'none', 'important');
767
+ });
768
+
769
+ // Select this option
770
+ element.classList.add('cf-checked');
771
+ element.style.setProperty('background-color', config.selectedBackground, 'important');
772
+ const tickIcon = element.querySelector('[data-chat-input-element="tick-icon"]');
773
+ if (tickIcon) tickIcon.style.setProperty('display', 'block', 'important');
774
+
775
+ if (isCustom) {
776
+ // Show min/max inputs
777
+ const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
778
+ if (rangeWrapper) {
779
+ rangeWrapper.style.display = '';
780
+ }
781
+
782
+ // Don't save data yet - wait for validation
783
+ chatState.currentSelection = null;
784
+ console.log(' ℹ️ Custom selected - waiting for min/max input');
785
+ } else {
786
+ // Hide min/max inputs
787
+ const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
788
+ if (rangeWrapper) {
789
+ rangeWrapper.style.display = 'none';
790
+ }
791
+
792
+ // Parse and save regular value
793
+ let value;
794
+ try {
795
+ value = JSON.parse(valueStr);
796
+ } catch (e) {
797
+ value = valueStr;
798
+ }
799
+
800
+ chatState.data[field] = value;
801
+ chatState.currentSelection = { field, value, name: optionName };
802
+ console.log(' ✅ Regular option selected:', value);
803
+ enableNextButton();
804
+ }
805
+ }
806
+
807
+ function validateMinMax(field, customConfig) {
808
+ const minInput = document.querySelector(`[data-field="${field}"][data-input-type="min"] [data-chat-input-element="input"]`);
809
+ const maxInput = document.querySelector(`[data-field="${field}"][data-input-type="max"] [data-chat-input-element="input"]`);
810
+ const errorDiv = document.querySelector(`[data-chat-element="range-error"][data-field="${field}"]`);
811
+
812
+ if (!minInput || !maxInput) return false;
813
+
814
+ const minValue = parseFloat(minInput.value);
815
+ const maxValue = parseFloat(maxInput.value);
816
+ const minFilled = minInput.value.trim() !== '';
817
+ const maxFilled = maxInput.value.trim() !== '';
818
+
819
+ const minConstraint = customConfig.min !== undefined ? customConfig.min : 0;
820
+ const maxConstraint = customConfig.max !== undefined ? customConfig.max : 100;
821
+
822
+ // Helper to show error
823
+ const showError = (message) => {
824
+ if (errorDiv) {
825
+ errorDiv.textContent = message;
826
+ errorDiv.style.display = 'block';
827
+ }
828
+ console.log(' ❌ Validation error:', message);
829
+ disableNextButton();
830
+ return false;
831
+ };
832
+
833
+ // Helper to hide error
834
+ const hideError = () => {
835
+ if (errorDiv) {
836
+ errorDiv.style.display = 'none';
837
+ }
838
+ };
839
+
840
+ // Validation rules
841
+
842
+ // 1. Check if only one is filled
843
+ if (minFilled && !maxFilled) {
844
+ return showError(config.customRangeErrors.maxRequired);
845
+ }
846
+
847
+ if (!minFilled && maxFilled) {
848
+ return showError(config.customRangeErrors.minRequired);
849
+ }
850
+
851
+ // 2. Check if both are empty
852
+ if (!minFilled && !maxFilled) {
853
+ hideError();
854
+ disableNextButton();
855
+ return false;
856
+ }
857
+
858
+ // 3. Check if values are valid numbers
859
+ if (isNaN(minValue) || isNaN(maxValue)) {
860
+ return showError(config.customRangeErrors.bothRequired);
861
+ }
862
+
863
+ // 4. Check min constraint
864
+ if (minValue < minConstraint) {
865
+ const msg = config.customRangeErrors.minBelowConstraint.replace('{min}', minConstraint);
866
+ return showError(msg);
867
+ }
868
+
869
+ // 5. Check max constraint
870
+ if (maxValue > maxConstraint) {
871
+ const msg = config.customRangeErrors.maxAboveConstraint.replace('{max}', maxConstraint);
872
+ return showError(msg);
873
+ }
874
+
875
+ // 6. Check min < max
876
+ if (minValue >= maxValue) {
877
+ return showError(config.customRangeErrors.minGreaterThanMax);
878
+ }
879
+
880
+ // All valid! Hide error and save data as array
881
+ hideError();
882
+
883
+ // Store as array [min, max]
884
+ chatState.data[field] = [minValue, maxValue];
885
+ chatState.currentSelection = {
886
+ field,
887
+ value: [minValue, maxValue],
888
+ name: `${minValue}-${maxValue}`
889
+ };
890
+
891
+ console.log(' ✅ Range valid - stored as array:', [minValue, maxValue]);
892
+ enableNextButton();
893
+ return true;
894
+ }
895
+
346
896
  // =============================================================================
347
897
  // TEXT/NUMBER INPUT RENDERING
348
898
  // =============================================================================
@@ -362,6 +912,10 @@ function renderTextInput(field, inputType = 'text') {
362
912
  return;
363
913
  }
364
914
 
915
+ // Get existing data for this field (for pre-filling when editing)
916
+ const existingValue = chatState.data[field];
917
+ console.log(`📝 Pre-filling ${field}:`, existingValue);
918
+
365
919
  // Clone existing input element
366
920
  const clone = existingInput.cloneNode(true);
367
921
 
@@ -376,7 +930,15 @@ function renderTextInput(field, inputType = 'text') {
376
930
  if (inputElement) {
377
931
  inputElement.setAttribute('data-field', field);
378
932
  inputElement.name = field;
379
- inputElement.value = '';
933
+
934
+ // Pre-fill value if it exists
935
+ if (existingValue !== undefined && existingValue !== null) {
936
+ inputElement.value = existingValue;
937
+ console.log(` ✅ Pre-filled with: ${existingValue}`);
938
+ } else {
939
+ inputElement.value = '';
940
+ }
941
+
380
942
  inputElement.type = inputType === 'number' ? 'number' : 'text';
381
943
 
382
944
  // Add input event to enable Next button when user types
@@ -668,6 +1230,28 @@ async function showNextStep() {
668
1230
  } else {
669
1231
  enableNextButton();
670
1232
  }
1233
+ } else if (inputType === 'multi-select-color') {
1234
+ // Render color options with color blocks
1235
+ renderColorOptions(nextStep.input.options, nextStep.input.field);
1236
+
1237
+ // Enable Next button if input not required (default behavior)
1238
+ if (!inputRequired) {
1239
+ enableNextButton();
1240
+ }
1241
+ } else if (inputType === 'single-select-custom') {
1242
+ // Render single-select with custom min/max option
1243
+ renderCustomSelectOptions(
1244
+ nextStep.input.options,
1245
+ nextStep.input.field,
1246
+ nextStep.input.custom
1247
+ );
1248
+
1249
+ // Disable Next button initially (wait for selection/validation)
1250
+ if (inputRequired) {
1251
+ disableNextButton();
1252
+ } else {
1253
+ enableNextButton();
1254
+ }
671
1255
  } else {
672
1256
  // Render options (single-select or multi-select)
673
1257
  const isSingleSelect = inputType === 'single-select';