lisichatbot 1.2.5 → 1.2.7
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/package.json +1 -1
- package/src/index.js +199 -33
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -145,7 +145,7 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
|
|
|
145
145
|
console.log(` Calling editStep(${stepNumber})...`);
|
|
146
146
|
editStep(stepNumber);
|
|
147
147
|
};
|
|
148
|
-
editIcon.style.setProperty('display', '
|
|
148
|
+
editIcon.style.setProperty('display', 'flex', 'important');
|
|
149
149
|
editIcon.style.setProperty('margin-left', '8px', 'important');
|
|
150
150
|
editIcon.style.setProperty('cursor', 'pointer', 'important');
|
|
151
151
|
console.log(` ✏️ Edit icon SHOWN (step ${stepNumber} is latest previous step with input)`);
|
|
@@ -184,7 +184,7 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
|
|
|
184
184
|
|
|
185
185
|
// Only show if has input AND it's a previous step AND it's latest instance
|
|
186
186
|
if (hasInput && stepNumber !== null && stepNumber < chatState.step && isLatest) {
|
|
187
|
-
editIconAfterAppend.style.setProperty('display', '
|
|
187
|
+
editIconAfterAppend.style.setProperty('display', 'flex', 'important');
|
|
188
188
|
|
|
189
189
|
// Debug: Check spacing
|
|
190
190
|
setTimeout(() => {
|
|
@@ -238,7 +238,7 @@ function updateEditIcons() {
|
|
|
238
238
|
editStep(stepNumber);
|
|
239
239
|
};
|
|
240
240
|
editIcon.setAttribute('data-chat-step', stepNumber);
|
|
241
|
-
editIcon.style.setProperty('display', '
|
|
241
|
+
editIcon.style.setProperty('display', 'flex', 'important');
|
|
242
242
|
editIcon.style.setProperty('cursor', 'pointer', 'important');
|
|
243
243
|
editIcon.style.setProperty('margin-left', '8px', 'important');
|
|
244
244
|
console.log(` ✏️ Step ${stepNumber}: Icon SHOWN (latest previous step)`);
|
|
@@ -282,6 +282,10 @@ function renderOptions(options, field, isSingleSelect = true) {
|
|
|
282
282
|
// Create wrapper to hold all options
|
|
283
283
|
const optionsWrapper = document.createElement('div');
|
|
284
284
|
optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
|
|
285
|
+
optionsWrapper.style.display = 'flex';
|
|
286
|
+
optionsWrapper.style.flexDirection = 'column';
|
|
287
|
+
optionsWrapper.style.alignItems = 'flex-start'; // Don't stretch options
|
|
288
|
+
optionsWrapper.style.gap = '8px';
|
|
285
289
|
|
|
286
290
|
// Clone and fill option element for each option
|
|
287
291
|
options.forEach((option, index) => {
|
|
@@ -396,6 +400,10 @@ function renderColorOptions(options, field) {
|
|
|
396
400
|
// Create wrapper to hold all options
|
|
397
401
|
const optionsWrapper = document.createElement('div');
|
|
398
402
|
optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
|
|
403
|
+
optionsWrapper.style.display = 'flex';
|
|
404
|
+
optionsWrapper.style.flexDirection = 'column';
|
|
405
|
+
optionsWrapper.style.alignItems = 'flex-start'; // Don't stretch options
|
|
406
|
+
optionsWrapper.style.gap = '8px';
|
|
399
407
|
|
|
400
408
|
// Clone and fill option element for each option
|
|
401
409
|
options.forEach((option, index) => {
|
|
@@ -514,6 +522,10 @@ function renderCustomSelectOptions(options, field, customConfig) {
|
|
|
514
522
|
// Create wrapper to hold all options
|
|
515
523
|
const optionsWrapper = document.createElement('div');
|
|
516
524
|
optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
|
|
525
|
+
optionsWrapper.style.display = 'flex';
|
|
526
|
+
optionsWrapper.style.flexDirection = 'column';
|
|
527
|
+
optionsWrapper.style.alignItems = 'flex-start'; // Don't stretch options
|
|
528
|
+
optionsWrapper.style.gap = '8px';
|
|
517
529
|
|
|
518
530
|
// Render regular options
|
|
519
531
|
options.forEach((option, index) => {
|
|
@@ -648,6 +660,10 @@ function renderMinMaxInputs(field, customConfig, existingData) {
|
|
|
648
660
|
const rangeWrapper = document.createElement('div');
|
|
649
661
|
rangeWrapper.setAttribute('data-chat-element', 'range-wrapper');
|
|
650
662
|
rangeWrapper.setAttribute('data-field', field);
|
|
663
|
+
rangeWrapper.style.marginBottom = '16px'; // Add space below wrapper
|
|
664
|
+
rangeWrapper.style.display = 'flex';
|
|
665
|
+
rangeWrapper.style.gap = '16px'; // Gap between min and max
|
|
666
|
+
rangeWrapper.style.flexWrap = 'wrap';
|
|
651
667
|
|
|
652
668
|
// Check if should show min/max (custom option selected)
|
|
653
669
|
// Data is stored as array [min, max]
|
|
@@ -712,14 +728,48 @@ function renderMinMaxInputs(field, customConfig, existingData) {
|
|
|
712
728
|
|
|
713
729
|
elements.messages.appendChild(rangeWrapper);
|
|
714
730
|
|
|
715
|
-
// Add
|
|
716
|
-
if (minInput) {
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
731
|
+
// Add handlers for both inputs
|
|
732
|
+
if (minInput && maxInput) {
|
|
733
|
+
const updateVisualFeedback = () => {
|
|
734
|
+
const minFilled = minInput.value.trim() !== '';
|
|
735
|
+
const maxFilled = maxInput.value.trim() !== '';
|
|
736
|
+
const anyFilled = minFilled || maxFilled;
|
|
737
|
+
|
|
738
|
+
// Get tick icons
|
|
739
|
+
const minTick = minClone.querySelector('[data-chat-input-element="tick-icon"]');
|
|
740
|
+
const maxTick = maxClone.querySelector('[data-chat-input-element="tick-icon"]');
|
|
741
|
+
|
|
742
|
+
// Update background and tick for BOTH inputs
|
|
743
|
+
if (anyFilled) {
|
|
744
|
+
minClone.style.backgroundColor = config.selectedBackground;
|
|
745
|
+
maxClone.style.backgroundColor = config.selectedBackground;
|
|
746
|
+
if (minTick) minTick.style.display = 'block';
|
|
747
|
+
if (maxTick) maxTick.style.display = 'block';
|
|
748
|
+
} else {
|
|
749
|
+
minClone.style.backgroundColor = 'transparent';
|
|
750
|
+
maxClone.style.backgroundColor = 'transparent';
|
|
751
|
+
if (minTick) minTick.style.display = 'none';
|
|
752
|
+
if (maxTick) maxTick.style.display = 'none';
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
|
|
756
|
+
minInput.onfocus = () => {
|
|
757
|
+
selectCustomOption(field);
|
|
758
|
+
updateVisualFeedback();
|
|
759
|
+
};
|
|
760
|
+
minInput.oninput = () => {
|
|
761
|
+
updateVisualFeedback();
|
|
762
|
+
validateMinMax(field, customConfig);
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
maxInput.onfocus = () => {
|
|
766
|
+
selectCustomOption(field);
|
|
767
|
+
updateVisualFeedback();
|
|
768
|
+
};
|
|
769
|
+
maxInput.oninput = () => {
|
|
770
|
+
updateVisualFeedback();
|
|
771
|
+
validateMinMax(field, customConfig);
|
|
772
|
+
};
|
|
723
773
|
}
|
|
724
774
|
}
|
|
725
775
|
|
|
@@ -783,10 +833,39 @@ function handleCustomSelectClick(element, field, customConfig) {
|
|
|
783
833
|
chatState.currentSelection = null;
|
|
784
834
|
console.log(' ℹ️ Custom selected - waiting for min/max input');
|
|
785
835
|
} else {
|
|
786
|
-
// Hide min/max inputs
|
|
836
|
+
// Hide min/max inputs and clear them
|
|
787
837
|
const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
|
|
788
838
|
if (rangeWrapper) {
|
|
789
839
|
rangeWrapper.style.display = 'none';
|
|
840
|
+
|
|
841
|
+
// Clear input values
|
|
842
|
+
const minInput = rangeWrapper.querySelector('[data-input-type="min"] [data-chat-input-element="input"]');
|
|
843
|
+
const maxInput = rangeWrapper.querySelector('[data-input-type="max"] [data-chat-input-element="input"]');
|
|
844
|
+
|
|
845
|
+
if (minInput) minInput.value = '';
|
|
846
|
+
if (maxInput) maxInput.value = '';
|
|
847
|
+
|
|
848
|
+
// Reset visual feedback (background and ticks)
|
|
849
|
+
const minContainer = rangeWrapper.querySelector('[data-chat-element="single-select-custom-min"]');
|
|
850
|
+
const maxContainer = rangeWrapper.querySelector('[data-chat-element="single-select-custom-max"]');
|
|
851
|
+
|
|
852
|
+
if (minContainer) {
|
|
853
|
+
minContainer.style.backgroundColor = 'transparent';
|
|
854
|
+
const minTick = minContainer.querySelector('[data-chat-input-element="tick-icon"]');
|
|
855
|
+
if (minTick) minTick.style.display = 'none';
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
if (maxContainer) {
|
|
859
|
+
maxContainer.style.backgroundColor = 'transparent';
|
|
860
|
+
const maxTick = maxContainer.querySelector('[data-chat-input-element="tick-icon"]');
|
|
861
|
+
if (maxTick) maxTick.style.display = 'none';
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// Hide error message
|
|
865
|
+
const errorDiv = rangeWrapper.querySelector('[data-chat-element="range-error"]');
|
|
866
|
+
if (errorDiv) errorDiv.style.display = 'none';
|
|
867
|
+
|
|
868
|
+
console.log(' 🧹 Min/max inputs cleared and hidden');
|
|
790
869
|
}
|
|
791
870
|
|
|
792
871
|
// Parse and save regular value
|
|
@@ -809,7 +888,7 @@ function validateMinMax(field, customConfig) {
|
|
|
809
888
|
const maxInput = document.querySelector(`[data-field="${field}"][data-input-type="max"] [data-chat-input-element="input"]`);
|
|
810
889
|
const errorDiv = document.querySelector(`[data-chat-element="range-error"][data-field="${field}"]`);
|
|
811
890
|
|
|
812
|
-
if (!minInput || !maxInput) return false;
|
|
891
|
+
if (!minInput || !maxInput) return { valid: false, error: null };
|
|
813
892
|
|
|
814
893
|
const minValue = parseFloat(minInput.value);
|
|
815
894
|
const maxValue = parseFloat(maxInput.value);
|
|
@@ -819,66 +898,91 @@ function validateMinMax(field, customConfig) {
|
|
|
819
898
|
const minConstraint = customConfig.min !== undefined ? customConfig.min : 0;
|
|
820
899
|
const maxConstraint = customConfig.max !== undefined ? customConfig.max : 100;
|
|
821
900
|
|
|
822
|
-
// Helper to show error
|
|
823
|
-
const
|
|
901
|
+
// Helper to show error in div and clear data
|
|
902
|
+
const showErrorDiv = (message) => {
|
|
824
903
|
if (errorDiv) {
|
|
825
904
|
errorDiv.textContent = message;
|
|
826
905
|
errorDiv.style.display = 'block';
|
|
827
906
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
907
|
+
// Clear the data when validation fails
|
|
908
|
+
delete chatState.data[field];
|
|
909
|
+
chatState.currentSelection = null;
|
|
831
910
|
};
|
|
832
911
|
|
|
833
|
-
// Helper to hide error
|
|
834
|
-
const
|
|
912
|
+
// Helper to hide error div
|
|
913
|
+
const hideErrorDiv = () => {
|
|
835
914
|
if (errorDiv) {
|
|
836
915
|
errorDiv.style.display = 'none';
|
|
837
916
|
}
|
|
838
917
|
};
|
|
839
918
|
|
|
840
|
-
// Validation rules
|
|
919
|
+
// Validation rules - return error message if invalid
|
|
841
920
|
|
|
842
921
|
// 1. Check if only one is filled
|
|
843
922
|
if (minFilled && !maxFilled) {
|
|
844
|
-
|
|
923
|
+
const error = config.customRangeErrors.maxRequired;
|
|
924
|
+
showErrorDiv(error);
|
|
925
|
+
console.log(' ❌ Validation error:', error);
|
|
926
|
+
disableNextButton();
|
|
927
|
+
return { valid: false, error };
|
|
845
928
|
}
|
|
846
929
|
|
|
847
930
|
if (!minFilled && maxFilled) {
|
|
848
|
-
|
|
931
|
+
const error = config.customRangeErrors.minRequired;
|
|
932
|
+
showErrorDiv(error);
|
|
933
|
+
console.log(' ❌ Validation error:', error);
|
|
934
|
+
disableNextButton();
|
|
935
|
+
return { valid: false, error };
|
|
849
936
|
}
|
|
850
937
|
|
|
851
938
|
// 2. Check if both are empty
|
|
852
939
|
if (!minFilled && !maxFilled) {
|
|
853
|
-
|
|
940
|
+
hideErrorDiv();
|
|
941
|
+
// Clear data when both empty
|
|
942
|
+
delete chatState.data[field];
|
|
943
|
+
chatState.currentSelection = null;
|
|
854
944
|
disableNextButton();
|
|
855
|
-
return false;
|
|
945
|
+
return { valid: false, error: null };
|
|
856
946
|
}
|
|
857
947
|
|
|
858
948
|
// 3. Check if values are valid numbers
|
|
859
949
|
if (isNaN(minValue) || isNaN(maxValue)) {
|
|
860
|
-
|
|
950
|
+
const error = config.customRangeErrors.bothRequired;
|
|
951
|
+
showErrorDiv(error);
|
|
952
|
+
console.log(' ❌ Validation error:', error);
|
|
953
|
+
disableNextButton();
|
|
954
|
+
return { valid: false, error };
|
|
861
955
|
}
|
|
862
956
|
|
|
863
957
|
// 4. Check min constraint
|
|
864
958
|
if (minValue < minConstraint) {
|
|
865
|
-
const
|
|
866
|
-
|
|
959
|
+
const error = config.customRangeErrors.minBelowConstraint.replace('{min}', minConstraint);
|
|
960
|
+
showErrorDiv(error);
|
|
961
|
+
console.log(' ❌ Validation error:', error);
|
|
962
|
+
disableNextButton();
|
|
963
|
+
return { valid: false, error };
|
|
867
964
|
}
|
|
868
965
|
|
|
869
966
|
// 5. Check max constraint
|
|
870
967
|
if (maxValue > maxConstraint) {
|
|
871
|
-
const
|
|
872
|
-
|
|
968
|
+
const error = config.customRangeErrors.maxAboveConstraint.replace('{max}', maxConstraint);
|
|
969
|
+
showErrorDiv(error);
|
|
970
|
+
console.log(' ❌ Validation error:', error);
|
|
971
|
+
disableNextButton();
|
|
972
|
+
return { valid: false, error };
|
|
873
973
|
}
|
|
874
974
|
|
|
875
975
|
// 6. Check min < max
|
|
876
976
|
if (minValue >= maxValue) {
|
|
877
|
-
|
|
977
|
+
const error = config.customRangeErrors.minGreaterThanMax;
|
|
978
|
+
showErrorDiv(error);
|
|
979
|
+
console.log(' ❌ Validation error:', error);
|
|
980
|
+
disableNextButton();
|
|
981
|
+
return { valid: false, error };
|
|
878
982
|
}
|
|
879
983
|
|
|
880
984
|
// All valid! Hide error and save data as array
|
|
881
|
-
|
|
985
|
+
hideErrorDiv();
|
|
882
986
|
|
|
883
987
|
// Store as array [min, max]
|
|
884
988
|
chatState.data[field] = [minValue, maxValue];
|
|
@@ -890,7 +994,7 @@ function validateMinMax(field, customConfig) {
|
|
|
890
994
|
|
|
891
995
|
console.log(' ✅ Range valid - stored as array:', [minValue, maxValue]);
|
|
892
996
|
enableNextButton();
|
|
893
|
-
return true;
|
|
997
|
+
return { valid: true, error: null };
|
|
894
998
|
}
|
|
895
999
|
|
|
896
1000
|
// =============================================================================
|
|
@@ -1106,6 +1210,54 @@ async function handleNext() {
|
|
|
1106
1210
|
return;
|
|
1107
1211
|
}
|
|
1108
1212
|
|
|
1213
|
+
// VALIDATION: Check for single-select-custom validation before proceeding
|
|
1214
|
+
if (currentStep.inputType === 'single-select-custom' && currentStep.input) {
|
|
1215
|
+
const field = currentStep.input.field;
|
|
1216
|
+
const customConfig = currentStep.input.custom;
|
|
1217
|
+
|
|
1218
|
+
// Check if custom range was selected (data is array)
|
|
1219
|
+
const selectedValue = chatState.data[field];
|
|
1220
|
+
const isCustomRange = Array.isArray(selectedValue);
|
|
1221
|
+
|
|
1222
|
+
if (isCustomRange && customConfig) {
|
|
1223
|
+
// Validate the custom range one more time before proceeding
|
|
1224
|
+
const validation = validateMinMax(field, customConfig);
|
|
1225
|
+
|
|
1226
|
+
if (!validation.valid) {
|
|
1227
|
+
// If there's an error message, show it
|
|
1228
|
+
if (validation.error) {
|
|
1229
|
+
console.log(' ⚠️ Validation failed in handleNext, showing error message');
|
|
1230
|
+
|
|
1231
|
+
// Hide current inputs (options and range wrapper)
|
|
1232
|
+
const optionsWrapper = document.querySelector('[data-chat-element="options-wrapper"]');
|
|
1233
|
+
if (optionsWrapper) {
|
|
1234
|
+
optionsWrapper.style.display = 'none';
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
|
|
1238
|
+
if (rangeWrapper) {
|
|
1239
|
+
rangeWrapper.style.display = 'none';
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// Add error message as bot message
|
|
1243
|
+
addMessage(validation.error, 'bot', false, null);
|
|
1244
|
+
|
|
1245
|
+
// Re-display the same step with fresh inputs
|
|
1246
|
+
await showNextStep();
|
|
1247
|
+
} else {
|
|
1248
|
+
// No error message, just incomplete data - do nothing
|
|
1249
|
+
console.log(' ⚠️ Validation incomplete, waiting for user input');
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
return; // Stop here, don't proceed
|
|
1253
|
+
}
|
|
1254
|
+
} else if (customConfig && chatState.currentSelection && chatState.currentSelection.value === customConfig.value) {
|
|
1255
|
+
// User selected the custom option but hasn't entered valid min/max yet
|
|
1256
|
+
console.log(' ⚠️ Custom option selected but no valid range entered');
|
|
1257
|
+
return; // Stop here
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1109
1261
|
// Call onNext validation if exists
|
|
1110
1262
|
if (currentStep.onNext) {
|
|
1111
1263
|
try {
|
|
@@ -1155,6 +1307,13 @@ async function handleNext() {
|
|
|
1155
1307
|
chatState.step = targetStep;
|
|
1156
1308
|
console.log(` ✅ Jumped to step ${targetStep}`);
|
|
1157
1309
|
|
|
1310
|
+
// Hide all range-wrappers
|
|
1311
|
+
const allRangeWrappers = document.querySelectorAll('[data-chat-element="range-wrapper"]');
|
|
1312
|
+
allRangeWrappers.forEach(wrapper => {
|
|
1313
|
+
wrapper.style.display = 'none';
|
|
1314
|
+
});
|
|
1315
|
+
console.log(' 🙈 Hidden all range-wrappers');
|
|
1316
|
+
|
|
1158
1317
|
// Update edit icons for the new position
|
|
1159
1318
|
updateEditIcons();
|
|
1160
1319
|
|
|
@@ -1169,6 +1328,13 @@ async function handleNext() {
|
|
|
1169
1328
|
// Normal flow: Move to next step
|
|
1170
1329
|
chatState.step++;
|
|
1171
1330
|
|
|
1331
|
+
// Hide all range-wrappers (custom min/max inputs)
|
|
1332
|
+
const allRangeWrappers = document.querySelectorAll('[data-chat-element="range-wrapper"]');
|
|
1333
|
+
allRangeWrappers.forEach(wrapper => {
|
|
1334
|
+
wrapper.style.display = 'none';
|
|
1335
|
+
});
|
|
1336
|
+
console.log(' 🙈 Hidden all range-wrappers');
|
|
1337
|
+
|
|
1172
1338
|
// Update edit icons for all previous steps
|
|
1173
1339
|
updateEditIcons();
|
|
1174
1340
|
|