lisichatbot 1.3.7 ā 1.3.9
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 +319 -287
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Conversational Flow Builder
|
|
3
3
|
* A simple, flexible conversational flow builder for multi-step chat interfaces
|
|
4
4
|
* @version 1.0.0
|
|
5
|
-
* WITH
|
|
5
|
+
* WITH MULTI-SELECT-DROPDOWN - COMPLETE FIXED VERSION
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
'use strict';
|
|
@@ -16,15 +16,15 @@ let chatState = {
|
|
|
16
16
|
data: {},
|
|
17
17
|
history: [],
|
|
18
18
|
currentSelection: null,
|
|
19
|
-
returnToStep: null,
|
|
20
|
-
completed: false
|
|
19
|
+
returnToStep: null,
|
|
20
|
+
completed: false
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
let elements = {
|
|
24
24
|
container: null,
|
|
25
25
|
messages: null,
|
|
26
26
|
nextBtn: null,
|
|
27
|
-
originalNextBtnText: null
|
|
27
|
+
originalNextBtnText: null
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
let config = {
|
|
@@ -58,16 +58,18 @@ function scrollToBottom() {
|
|
|
58
58
|
function enableNextButton() {
|
|
59
59
|
if (elements.nextBtn) {
|
|
60
60
|
elements.nextBtn.disabled = false;
|
|
61
|
-
elements.nextBtn.style.opacity
|
|
62
|
-
elements.nextBtn.style.cursor
|
|
61
|
+
elements.nextBtn.style.setProperty('opacity', '1', 'important');
|
|
62
|
+
elements.nextBtn.style.setProperty('cursor', 'pointer', 'important');
|
|
63
|
+
console.log(' ā
Next button ENABLED (opacity: 1, cursor: pointer)');
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
function disableNextButton() {
|
|
67
68
|
if (elements.nextBtn) {
|
|
68
69
|
elements.nextBtn.disabled = true;
|
|
69
|
-
elements.nextBtn.style.opacity
|
|
70
|
-
elements.nextBtn.style.cursor
|
|
70
|
+
elements.nextBtn.style.setProperty('opacity', '0.5', 'important');
|
|
71
|
+
elements.nextBtn.style.setProperty('cursor', 'not-allowed', 'important');
|
|
72
|
+
console.log(' š Next button DISABLED (opacity: 0.5, cursor: not-allowed)');
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
@@ -78,7 +80,6 @@ function disableNextButton() {
|
|
|
78
80
|
function addMessage(content, type = 'bot', hasInput = false, stepNumber = null) {
|
|
79
81
|
if (!elements.messages) return;
|
|
80
82
|
|
|
81
|
-
// Find the wrapper element directly by data-chat-element
|
|
82
83
|
const wrapperSelector = `[data-chat-element="${type}-message-wrapper"]`;
|
|
83
84
|
const existingWrapper = document.querySelector(wrapperSelector);
|
|
84
85
|
|
|
@@ -87,20 +88,16 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
|
|
|
87
88
|
return;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
// Clone the existing wrapper
|
|
91
91
|
const clone = existingWrapper.cloneNode(true);
|
|
92
92
|
|
|
93
|
-
// Immediately hide any edit icons in the clone (regardless of template state)
|
|
94
93
|
const allEditIcons = clone.querySelectorAll('[data-chat-element="bot-edit-icon"]');
|
|
95
94
|
allEditIcons.forEach(icon => {
|
|
96
95
|
icon.style.setProperty('display', 'none', 'important');
|
|
97
96
|
});
|
|
98
97
|
|
|
99
|
-
// Add step number if provided
|
|
100
98
|
if (stepNumber !== null) {
|
|
101
99
|
clone.setAttribute('data-chat-step', stepNumber);
|
|
102
100
|
|
|
103
|
-
// Mark all previous instances of this step as NOT latest
|
|
104
101
|
const previousInstances = elements.messages.querySelectorAll(
|
|
105
102
|
`[data-chat-element="${type}-message-wrapper"][data-chat-step="${stepNumber}"]`
|
|
106
103
|
);
|
|
@@ -108,11 +105,9 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
|
|
|
108
105
|
prev.removeAttribute('data-chat-latest');
|
|
109
106
|
});
|
|
110
107
|
|
|
111
|
-
// Mark this as the latest instance of this step
|
|
112
108
|
clone.setAttribute('data-chat-latest', 'true');
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
// Find text element in clone and set content
|
|
116
111
|
const textElement = clone.querySelector(`[data-chat-element="${type}-message-text"]`);
|
|
117
112
|
if (textElement) {
|
|
118
113
|
textElement.textContent = content;
|
|
@@ -120,7 +115,6 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
|
|
|
120
115
|
console.error(`Element with data-chat-element="${type}-message-text" not found in wrapper`);
|
|
121
116
|
}
|
|
122
117
|
|
|
123
|
-
// Handle edit icon for bot messages
|
|
124
118
|
if (type === 'bot') {
|
|
125
119
|
console.log(`\nš Processing bot message - Step ${stepNumber || 'N/A'}, hasInput: ${hasInput}`);
|
|
126
120
|
|
|
@@ -128,14 +122,11 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
|
|
|
128
122
|
if (editIcon) {
|
|
129
123
|
console.log(' ā Edit icon element found');
|
|
130
124
|
|
|
131
|
-
// ALWAYS hide first with !important to override any CSS
|
|
132
125
|
editIcon.style.setProperty('display', 'none', 'important');
|
|
133
126
|
console.log(' ā Edit icon hidden (display: none !important)');
|
|
134
127
|
|
|
135
|
-
// Check if this is the latest instance (it should be since we just marked it)
|
|
136
128
|
const isLatest = clone.hasAttribute('data-chat-latest');
|
|
137
129
|
|
|
138
|
-
// Only show if this step has input AND it's a PREVIOUS step AND it's the latest instance
|
|
139
130
|
if (hasInput && stepNumber !== null && stepNumber < chatState.step && isLatest) {
|
|
140
131
|
editIcon.setAttribute('data-chat-step', stepNumber);
|
|
141
132
|
editIcon.onclick = (e) => {
|
|
@@ -163,13 +154,9 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
|
|
|
163
154
|
}
|
|
164
155
|
}
|
|
165
156
|
|
|
166
|
-
// Make clone visible
|
|
167
157
|
clone.style.display = '';
|
|
168
|
-
|
|
169
|
-
// Append to messages container
|
|
170
158
|
elements.messages.appendChild(clone);
|
|
171
159
|
|
|
172
|
-
// IMPORTANT: Set edit icon display AFTER appending to DOM
|
|
173
160
|
if (type === 'bot') {
|
|
174
161
|
const editIconAfterAppend = clone.querySelector('[data-chat-element="bot-edit-icon"]');
|
|
175
162
|
if (editIconAfterAppend) {
|
|
@@ -246,206 +233,7 @@ function updateEditIcons() {
|
|
|
246
233
|
}
|
|
247
234
|
|
|
248
235
|
// =============================================================================
|
|
249
|
-
//
|
|
250
|
-
// =============================================================================
|
|
251
|
-
|
|
252
|
-
function renderOptions(options, field, isSingleSelect = true) {
|
|
253
|
-
if (!elements.messages) return;
|
|
254
|
-
|
|
255
|
-
const inputTypeAttr = isSingleSelect ? 'single-select-input' : 'multi-select-input';
|
|
256
|
-
const optionSelector = `[data-chat-element="${inputTypeAttr}"]`;
|
|
257
|
-
const existingOption = document.querySelector(optionSelector);
|
|
258
|
-
|
|
259
|
-
if (!existingOption) {
|
|
260
|
-
console.error(`Element with ${optionSelector} not found in HTML. Please add it to your HTML.`);
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const existingData = chatState.data[field];
|
|
265
|
-
console.log(`š Pre-filling ${field}:`, existingData);
|
|
266
|
-
|
|
267
|
-
const optionsWrapper = document.createElement('div');
|
|
268
|
-
optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
|
|
269
|
-
optionsWrapper.style.display = 'flex';
|
|
270
|
-
optionsWrapper.style.flexDirection = 'column';
|
|
271
|
-
optionsWrapper.style.alignItems = 'flex-start';
|
|
272
|
-
optionsWrapper.style.gap = '8px';
|
|
273
|
-
|
|
274
|
-
options.forEach((option, index) => {
|
|
275
|
-
const optionName = option.name || option;
|
|
276
|
-
const optionValue = option.value !== undefined ? option.value : option;
|
|
277
|
-
const valueStr = typeof optionValue === 'object' ?
|
|
278
|
-
JSON.stringify(optionValue) : String(optionValue);
|
|
279
|
-
|
|
280
|
-
const clone = existingOption.cloneNode(true);
|
|
281
|
-
clone.style.display = '';
|
|
282
|
-
|
|
283
|
-
let shouldBeChecked = false;
|
|
284
|
-
if (isSingleSelect) {
|
|
285
|
-
shouldBeChecked = existingData !== undefined &&
|
|
286
|
-
JSON.stringify(existingData) === JSON.stringify(optionValue);
|
|
287
|
-
} else {
|
|
288
|
-
shouldBeChecked = Array.isArray(existingData) &&
|
|
289
|
-
existingData.some(v => JSON.stringify(v) === JSON.stringify(optionValue));
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
if (shouldBeChecked) {
|
|
293
|
-
clone.classList.add('cf-checked');
|
|
294
|
-
clone.style.backgroundColor = config.selectedBackground;
|
|
295
|
-
console.log(` ā
Pre-selected: ${optionName}`);
|
|
296
|
-
} else {
|
|
297
|
-
clone.classList.remove('cf-checked');
|
|
298
|
-
clone.style.backgroundColor = 'transparent';
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
clone.setAttribute('data-chat-element', inputTypeAttr);
|
|
302
|
-
clone.setAttribute('data-field', field);
|
|
303
|
-
clone.setAttribute('data-value', valueStr);
|
|
304
|
-
clone.setAttribute('data-name', optionName);
|
|
305
|
-
clone.setAttribute('data-index', index);
|
|
306
|
-
|
|
307
|
-
const input = clone.querySelector('[data-chat-input-element="input"]');
|
|
308
|
-
if (input) {
|
|
309
|
-
input.setAttribute('data-chat-element', inputTypeAttr);
|
|
310
|
-
input.name = field;
|
|
311
|
-
input.value = valueStr;
|
|
312
|
-
input.checked = shouldBeChecked;
|
|
313
|
-
input.onclick = (e) => e.stopPropagation();
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const tickIcon = clone.querySelector('[data-chat-input-element="tick-icon"]');
|
|
317
|
-
if (tickIcon) {
|
|
318
|
-
tickIcon.style.display = shouldBeChecked ? 'block' : 'none';
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const textElement = clone.querySelector('[data-chat-input-element="text"]');
|
|
322
|
-
if (textElement) {
|
|
323
|
-
textElement.textContent = optionName;
|
|
324
|
-
textElement.style.display = '';
|
|
325
|
-
} else {
|
|
326
|
-
console.error('Text element not found in option');
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
optionsWrapper.appendChild(clone);
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
elements.messages.appendChild(optionsWrapper);
|
|
333
|
-
scrollToBottom();
|
|
334
|
-
|
|
335
|
-
const optionElements = optionsWrapper.querySelectorAll('[data-chat-input-element="container"]');
|
|
336
|
-
optionElements.forEach(el => {
|
|
337
|
-
el.onclick = null;
|
|
338
|
-
el.onclick = (e) => {
|
|
339
|
-
e.stopPropagation();
|
|
340
|
-
e.preventDefault();
|
|
341
|
-
handleOptionClick(el, field, isSingleSelect);
|
|
342
|
-
};
|
|
343
|
-
});
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// =============================================================================
|
|
347
|
-
// MULTI-SELECT-COLOR OPTIONS RENDERING
|
|
348
|
-
// =============================================================================
|
|
349
|
-
|
|
350
|
-
function renderColorOptions(options, field) {
|
|
351
|
-
if (!elements.messages) return;
|
|
352
|
-
|
|
353
|
-
const optionSelector = '[data-chat-element="multi-select-color"]';
|
|
354
|
-
const existingOption = document.querySelector(optionSelector);
|
|
355
|
-
|
|
356
|
-
if (!existingOption) {
|
|
357
|
-
console.error(`Element with ${optionSelector} not found in HTML. Please add it to your HTML.`);
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const existingData = chatState.data[field];
|
|
362
|
-
console.log(`š Pre-filling ${field} (color):`, existingData);
|
|
363
|
-
|
|
364
|
-
const optionsWrapper = document.createElement('div');
|
|
365
|
-
optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
|
|
366
|
-
optionsWrapper.style.display = 'flex';
|
|
367
|
-
optionsWrapper.style.flexDirection = 'column';
|
|
368
|
-
optionsWrapper.style.alignItems = 'flex-start';
|
|
369
|
-
optionsWrapper.style.gap = '8px';
|
|
370
|
-
|
|
371
|
-
options.forEach((option, index) => {
|
|
372
|
-
const optionName = option.name || option;
|
|
373
|
-
const optionValue = option.value !== undefined ? option.value : option;
|
|
374
|
-
const optionColor = option.color || '#cccccc';
|
|
375
|
-
const valueStr = typeof optionValue === 'object' ?
|
|
376
|
-
JSON.stringify(optionValue) : String(optionValue);
|
|
377
|
-
|
|
378
|
-
const clone = existingOption.cloneNode(true);
|
|
379
|
-
clone.style.display = '';
|
|
380
|
-
|
|
381
|
-
const shouldBeChecked = Array.isArray(existingData) &&
|
|
382
|
-
existingData.some(v => JSON.stringify(v) === JSON.stringify(optionValue));
|
|
383
|
-
|
|
384
|
-
if (shouldBeChecked) {
|
|
385
|
-
clone.classList.add('cf-checked');
|
|
386
|
-
clone.style.backgroundColor = config.selectedBackground;
|
|
387
|
-
console.log(` ā
Pre-selected color: ${optionName}`);
|
|
388
|
-
} else {
|
|
389
|
-
clone.classList.remove('cf-checked');
|
|
390
|
-
clone.style.backgroundColor = 'transparent';
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
clone.setAttribute('data-chat-element', 'multi-select-color');
|
|
394
|
-
clone.setAttribute('data-field', field);
|
|
395
|
-
clone.setAttribute('data-value', valueStr);
|
|
396
|
-
clone.setAttribute('data-name', optionName);
|
|
397
|
-
clone.setAttribute('data-index', index);
|
|
398
|
-
clone.setAttribute('data-color', optionColor);
|
|
399
|
-
|
|
400
|
-
const input = clone.querySelector('[data-chat-input-element="input"]');
|
|
401
|
-
if (input) {
|
|
402
|
-
input.setAttribute('data-chat-element', 'multi-select-color');
|
|
403
|
-
input.name = field;
|
|
404
|
-
input.value = valueStr;
|
|
405
|
-
input.checked = shouldBeChecked;
|
|
406
|
-
input.onclick = (e) => e.stopPropagation();
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const tickIcon = clone.querySelector('[data-chat-input-element="tick-icon"]');
|
|
410
|
-
if (tickIcon) {
|
|
411
|
-
tickIcon.style.display = shouldBeChecked ? 'block' : 'none';
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const colorBlock = clone.querySelector('[data-chat-input-element="color-block"]');
|
|
415
|
-
if (colorBlock) {
|
|
416
|
-
colorBlock.style.backgroundColor = optionColor;
|
|
417
|
-
colorBlock.style.display = '';
|
|
418
|
-
} else {
|
|
419
|
-
console.warn('Color block element not found in multi-select-color template');
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const textElement = clone.querySelector('[data-chat-input-element="text"]');
|
|
423
|
-
if (textElement) {
|
|
424
|
-
textElement.textContent = optionName;
|
|
425
|
-
textElement.style.display = '';
|
|
426
|
-
} else {
|
|
427
|
-
console.error('Text element not found in option');
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
optionsWrapper.appendChild(clone);
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
elements.messages.appendChild(optionsWrapper);
|
|
434
|
-
scrollToBottom();
|
|
435
|
-
|
|
436
|
-
const optionElements = optionsWrapper.querySelectorAll('[data-chat-input-element="container"]');
|
|
437
|
-
optionElements.forEach(el => {
|
|
438
|
-
el.onclick = null;
|
|
439
|
-
el.onclick = (e) => {
|
|
440
|
-
e.stopPropagation();
|
|
441
|
-
e.preventDefault();
|
|
442
|
-
handleOptionClick(el, field, false);
|
|
443
|
-
};
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// =============================================================================
|
|
448
|
-
// MULTI-SELECT DROPDOWN RENDERING
|
|
236
|
+
// MULTI-SELECT DROPDOWN RENDERING - COMPLETE FIXED VERSION
|
|
449
237
|
// =============================================================================
|
|
450
238
|
|
|
451
239
|
function renderMultiSelectDropdown(options, field) {
|
|
@@ -454,44 +242,48 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
454
242
|
console.log(`\nšØ renderMultiSelectDropdown for field: ${field}`);
|
|
455
243
|
console.log(`Options:`, options);
|
|
456
244
|
|
|
457
|
-
// Find template
|
|
458
245
|
const template = document.querySelector('[data-chat-element="multi-select-dropdown"]');
|
|
459
246
|
if (!template) {
|
|
460
247
|
console.error('multi-select-dropdown template not found');
|
|
461
248
|
return;
|
|
462
249
|
}
|
|
463
250
|
|
|
464
|
-
// Clone template
|
|
465
251
|
const clone = template.cloneNode(true);
|
|
466
252
|
clone.style.display = '';
|
|
467
253
|
clone.setAttribute('data-field', field);
|
|
468
254
|
|
|
469
|
-
// Get all elements
|
|
470
255
|
const dropdown = clone.querySelector('[data-chat-input-element="dropdown"]');
|
|
471
256
|
const optionsWrapper = clone.querySelector('[data-chat-input-element="dropdown-options-wrapper"]');
|
|
472
257
|
const searchBar = clone.querySelector('[data-chat-input-element="search-bar"]');
|
|
473
|
-
const optionsContainer = clone.querySelector('
|
|
258
|
+
const optionsContainer = clone.querySelector('.lisi-app--dropdown_options-list');
|
|
474
259
|
const optionTemplate = clone.querySelector('[data-chat-input-element="dropdown-option-wrapper"]');
|
|
475
260
|
const tagsContainer = clone.querySelector('[data-chat-input-element="tags-container"]');
|
|
476
261
|
const tagTemplate = clone.querySelector('[data-chat-input-element="tag-wrapper"]');
|
|
477
262
|
|
|
478
263
|
if (!dropdown || !optionsWrapper || !searchBar || !optionsContainer || !optionTemplate || !tagsContainer || !tagTemplate) {
|
|
479
264
|
console.error('Multi-select dropdown: Missing required elements');
|
|
265
|
+
console.error({
|
|
266
|
+
dropdown: !!dropdown,
|
|
267
|
+
optionsWrapper: !!optionsWrapper,
|
|
268
|
+
searchBar: !!searchBar,
|
|
269
|
+
optionsContainer: !!optionsContainer,
|
|
270
|
+
optionTemplate: !!optionTemplate,
|
|
271
|
+
tagsContainer: !!tagsContainer,
|
|
272
|
+
tagTemplate: !!tagTemplate
|
|
273
|
+
});
|
|
480
274
|
return;
|
|
481
275
|
}
|
|
482
276
|
|
|
483
|
-
// Store original template option and tag, then remove from DOM
|
|
484
277
|
const optionTemplateClone = optionTemplate.cloneNode(true);
|
|
485
278
|
const tagTemplateClone = tagTemplate.cloneNode(true);
|
|
486
279
|
optionTemplate.remove();
|
|
487
280
|
tagTemplate.remove();
|
|
488
281
|
|
|
489
|
-
// Get existing
|
|
282
|
+
// ā
Get existing data for pre-filling (edit mode)
|
|
490
283
|
const existingData = chatState.data[field] || [];
|
|
491
284
|
const selectedValues = new Set(existingData);
|
|
492
|
-
console.log(`š Pre-filling selections:`, Array.from(selectedValues));
|
|
285
|
+
console.log(`š Pre-filling selections for ${field}:`, Array.from(selectedValues));
|
|
493
286
|
|
|
494
|
-
// Store all options for filtering
|
|
495
287
|
const allOptions = [];
|
|
496
288
|
|
|
497
289
|
// Render options
|
|
@@ -506,15 +298,21 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
506
298
|
|
|
507
299
|
if (nameEl) nameEl.textContent = option.name;
|
|
508
300
|
|
|
509
|
-
// Check if pre-selected
|
|
301
|
+
// ā
Check if pre-selected (for edit mode)
|
|
510
302
|
const isSelected = selectedValues.has(option.value);
|
|
511
303
|
if (isSelected) {
|
|
512
304
|
optionEl.style.backgroundColor = config.selectedBackground;
|
|
513
|
-
if (tickIcon)
|
|
305
|
+
if (tickIcon) {
|
|
306
|
+
// ā
FIX: Use display: block instead of empty string
|
|
307
|
+
tickIcon.style.display = 'block';
|
|
308
|
+
}
|
|
514
309
|
console.log(` ā
Pre-selected: ${option.name}`);
|
|
310
|
+
} else {
|
|
311
|
+
if (tickIcon) {
|
|
312
|
+
tickIcon.style.display = 'none';
|
|
313
|
+
}
|
|
515
314
|
}
|
|
516
315
|
|
|
517
|
-
// Click handler - prevents dropdown from closing
|
|
518
316
|
optionEl.onclick = (e) => {
|
|
519
317
|
e.stopPropagation();
|
|
520
318
|
toggleOption(option, optionEl, tickIcon, field);
|
|
@@ -524,7 +322,6 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
524
322
|
allOptions.push({ element: optionEl, option });
|
|
525
323
|
});
|
|
526
324
|
|
|
527
|
-
// Toggle dropdown visibility - click to open/close
|
|
528
325
|
dropdown.onclick = (e) => {
|
|
529
326
|
e.stopPropagation();
|
|
530
327
|
const isVisible = optionsWrapper.style.display !== 'none';
|
|
@@ -547,7 +344,6 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
547
344
|
}
|
|
548
345
|
};
|
|
549
346
|
|
|
550
|
-
// Close dropdown when clicking outside
|
|
551
347
|
const closeDropdownHandler = (e) => {
|
|
552
348
|
if (!clone.contains(e.target)) {
|
|
553
349
|
if (optionsWrapper.style.display !== 'none') {
|
|
@@ -560,7 +356,6 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
560
356
|
document.addEventListener('click', closeDropdownHandler);
|
|
561
357
|
clone.dataset.closeHandler = 'attached';
|
|
562
358
|
|
|
563
|
-
// Search functionality - filters options by name or value
|
|
564
359
|
searchBar.oninput = (e) => {
|
|
565
360
|
const searchTerm = e.target.value.toLowerCase();
|
|
566
361
|
console.log(`š Searching for: "${searchTerm}"`);
|
|
@@ -574,12 +369,10 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
574
369
|
});
|
|
575
370
|
};
|
|
576
371
|
|
|
577
|
-
// Prevent search bar clicks from closing dropdown
|
|
578
372
|
searchBar.onclick = (e) => {
|
|
579
373
|
e.stopPropagation();
|
|
580
374
|
};
|
|
581
375
|
|
|
582
|
-
// Toggle option selection
|
|
583
376
|
function toggleOption(option, optionEl, tickIcon, field) {
|
|
584
377
|
const currentSelections = chatState.data[field] || [];
|
|
585
378
|
const isCurrentlySelected = currentSelections.includes(option.value);
|
|
@@ -587,20 +380,23 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
587
380
|
console.log(`šÆ Toggle option: ${option.name}`, { isCurrentlySelected });
|
|
588
381
|
|
|
589
382
|
if (isCurrentlySelected) {
|
|
590
|
-
// Deselect
|
|
591
383
|
chatState.data[field] = currentSelections.filter(v => v !== option.value);
|
|
592
384
|
optionEl.style.backgroundColor = '';
|
|
593
|
-
if (tickIcon)
|
|
385
|
+
if (tickIcon) {
|
|
386
|
+
// ā
FIX: Explicitly set to 'none'
|
|
387
|
+
tickIcon.style.display = 'none';
|
|
388
|
+
}
|
|
594
389
|
console.log(` ā Deselected: ${option.name}`);
|
|
595
390
|
} else {
|
|
596
|
-
// Select
|
|
597
391
|
chatState.data[field] = [...currentSelections, option.value];
|
|
598
392
|
optionEl.style.backgroundColor = config.selectedBackground;
|
|
599
|
-
if (tickIcon)
|
|
393
|
+
if (tickIcon) {
|
|
394
|
+
// ā
FIX: Explicitly set to 'block'
|
|
395
|
+
tickIcon.style.display = 'block';
|
|
396
|
+
}
|
|
600
397
|
console.log(` ā
Selected: ${option.name}`);
|
|
601
398
|
}
|
|
602
399
|
|
|
603
|
-
// Update current selection for handleNext
|
|
604
400
|
chatState.currentSelection = {
|
|
605
401
|
field,
|
|
606
402
|
value: chatState.data[field],
|
|
@@ -612,20 +408,24 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
612
408
|
|
|
613
409
|
console.log(` š Current selections:`, chatState.data[field]);
|
|
614
410
|
|
|
615
|
-
// Re-render tags
|
|
616
411
|
renderTags(field, options, tagsContainer, tagTemplateClone, optionsContainer);
|
|
617
412
|
|
|
618
|
-
// Enable Next button if at least one selected
|
|
619
413
|
if (chatState.data[field].length > 0) {
|
|
620
414
|
enableNextButton();
|
|
621
415
|
} else {
|
|
622
|
-
|
|
416
|
+
// ā
FIX: Check if input is required before disabling
|
|
417
|
+
const currentStep = flowData.flow[chatState.step];
|
|
418
|
+
const inputRequired = currentStep.inputRequired === true;
|
|
419
|
+
|
|
420
|
+
if (inputRequired) {
|
|
421
|
+
disableNextButton();
|
|
422
|
+
} else {
|
|
423
|
+
enableNextButton();
|
|
424
|
+
}
|
|
623
425
|
}
|
|
624
426
|
}
|
|
625
427
|
|
|
626
|
-
// Render tags - displays selected options as removable tags
|
|
627
428
|
function renderTags(field, options, tagsContainer, tagTemplate, optionsContainer) {
|
|
628
|
-
// Clear existing tags (except template)
|
|
629
429
|
const existingTags = tagsContainer.querySelectorAll('[data-chat-input-element="tag-wrapper"]');
|
|
630
430
|
existingTags.forEach(tag => {
|
|
631
431
|
if (tag.style.display !== 'none') {
|
|
@@ -651,32 +451,28 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
651
451
|
textEl.textContent = option.name;
|
|
652
452
|
}
|
|
653
453
|
|
|
654
|
-
// ā
CRITICAL: Set cross icon display to BLOCK and handle click
|
|
655
454
|
if (crossIcon) {
|
|
656
|
-
// Set display to block (as requested by user)
|
|
657
455
|
crossIcon.style.display = 'block';
|
|
658
456
|
console.log(` ā
Cross icon visible for: ${option.name}`);
|
|
659
457
|
|
|
660
|
-
// Handle click to remove tag
|
|
661
458
|
crossIcon.onclick = (e) => {
|
|
662
459
|
e.stopPropagation();
|
|
663
460
|
console.log(`šļø Removing tag: ${option.name}`);
|
|
664
461
|
|
|
665
|
-
// Remove from selections array
|
|
666
462
|
chatState.data[field] = chatState.data[field].filter(v => v !== value);
|
|
667
463
|
|
|
668
|
-
// Update option UI in dropdown - remove background and tick
|
|
669
464
|
const optionEl = optionsContainer.querySelector(`[data-value='${JSON.stringify(value)}']`);
|
|
670
465
|
if (optionEl) {
|
|
671
466
|
optionEl.style.backgroundColor = '';
|
|
672
467
|
const tickIcon = optionEl.querySelector('[data-chat-input-element="dropdown-option-tick-icon"]');
|
|
673
|
-
if (tickIcon)
|
|
468
|
+
if (tickIcon) {
|
|
469
|
+
// ā
FIX: Explicitly set to 'none'
|
|
470
|
+
tickIcon.style.display = 'none';
|
|
471
|
+
}
|
|
674
472
|
}
|
|
675
473
|
|
|
676
|
-
// Remove tag from DOM
|
|
677
474
|
tagEl.remove();
|
|
678
475
|
|
|
679
|
-
// Update current selection
|
|
680
476
|
chatState.currentSelection = {
|
|
681
477
|
field,
|
|
682
478
|
value: chatState.data[field],
|
|
@@ -688,9 +484,16 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
688
484
|
|
|
689
485
|
console.log(` š Remaining selections:`, chatState.data[field]);
|
|
690
486
|
|
|
691
|
-
// Disable Next button if no selections remain
|
|
692
487
|
if (chatState.data[field].length === 0) {
|
|
693
|
-
|
|
488
|
+
// ā
FIX: Check if input is required before disabling
|
|
489
|
+
const currentStep = flowData.flow[chatState.step];
|
|
490
|
+
const inputRequired = currentStep.inputRequired === true;
|
|
491
|
+
|
|
492
|
+
if (inputRequired) {
|
|
493
|
+
disableNextButton();
|
|
494
|
+
} else {
|
|
495
|
+
enableNextButton();
|
|
496
|
+
}
|
|
694
497
|
}
|
|
695
498
|
};
|
|
696
499
|
}
|
|
@@ -699,19 +502,217 @@ function renderMultiSelectDropdown(options, field) {
|
|
|
699
502
|
});
|
|
700
503
|
}
|
|
701
504
|
|
|
702
|
-
// Initial tag render if pre-filled
|
|
505
|
+
// ā
Initial tag render if pre-filled (edit mode)
|
|
703
506
|
if (selectedValues.size > 0) {
|
|
704
507
|
renderTags(field, options, tagsContainer, tagTemplateClone, optionsContainer);
|
|
705
|
-
|
|
508
|
+
console.log(` ā
Pre-filled ${selectedValues.size} selections, Next button enabled`);
|
|
706
509
|
}
|
|
707
510
|
|
|
708
|
-
// Append to messages
|
|
709
511
|
elements.messages.appendChild(clone);
|
|
710
512
|
scrollToBottom();
|
|
711
513
|
|
|
712
514
|
console.log(`ā
Multi-select dropdown rendered for ${field}`);
|
|
713
515
|
}
|
|
714
516
|
|
|
517
|
+
// =============================================================================
|
|
518
|
+
// OPTIONS RENDERING WITH CUSTOM ATTRIBUTES
|
|
519
|
+
// =============================================================================
|
|
520
|
+
|
|
521
|
+
function renderOptions(options, field, isSingleSelect = true) {
|
|
522
|
+
if (!elements.messages) return;
|
|
523
|
+
|
|
524
|
+
const inputTypeAttr = isSingleSelect ? 'single-select-input' : 'multi-select-input';
|
|
525
|
+
const optionSelector = `[data-chat-element="${inputTypeAttr}"]`;
|
|
526
|
+
const existingOption = document.querySelector(optionSelector);
|
|
527
|
+
|
|
528
|
+
if (!existingOption) {
|
|
529
|
+
console.error(`Element with ${optionSelector} not found in HTML. Please add it to your HTML.`);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const existingData = chatState.data[field];
|
|
534
|
+
console.log(`š Pre-filling ${field}:`, existingData);
|
|
535
|
+
|
|
536
|
+
const optionsWrapper = document.createElement('div');
|
|
537
|
+
optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
|
|
538
|
+
optionsWrapper.style.display = 'flex';
|
|
539
|
+
optionsWrapper.style.flexDirection = 'column';
|
|
540
|
+
optionsWrapper.style.alignItems = 'flex-start';
|
|
541
|
+
optionsWrapper.style.gap = '8px';
|
|
542
|
+
|
|
543
|
+
options.forEach((option, index) => {
|
|
544
|
+
const optionName = option.name || option;
|
|
545
|
+
const optionValue = option.value !== undefined ? option.value : option;
|
|
546
|
+
const valueStr = typeof optionValue === 'object' ?
|
|
547
|
+
JSON.stringify(optionValue) : String(optionValue);
|
|
548
|
+
|
|
549
|
+
const clone = existingOption.cloneNode(true);
|
|
550
|
+
clone.style.display = '';
|
|
551
|
+
|
|
552
|
+
let shouldBeChecked = false;
|
|
553
|
+
if (isSingleSelect) {
|
|
554
|
+
shouldBeChecked = existingData !== undefined &&
|
|
555
|
+
JSON.stringify(existingData) === JSON.stringify(optionValue);
|
|
556
|
+
} else {
|
|
557
|
+
shouldBeChecked = Array.isArray(existingData) &&
|
|
558
|
+
existingData.some(v => JSON.stringify(v) === JSON.stringify(optionValue));
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (shouldBeChecked) {
|
|
562
|
+
clone.classList.add('cf-checked');
|
|
563
|
+
clone.style.backgroundColor = config.selectedBackground;
|
|
564
|
+
console.log(` ā
Pre-selected: ${optionName}`);
|
|
565
|
+
} else {
|
|
566
|
+
clone.classList.remove('cf-checked');
|
|
567
|
+
clone.style.backgroundColor = 'transparent';
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
clone.setAttribute('data-chat-element', inputTypeAttr);
|
|
571
|
+
clone.setAttribute('data-field', field);
|
|
572
|
+
clone.setAttribute('data-value', valueStr);
|
|
573
|
+
clone.setAttribute('data-name', optionName);
|
|
574
|
+
clone.setAttribute('data-index', index);
|
|
575
|
+
|
|
576
|
+
const input = clone.querySelector('[data-chat-input-element="input"]');
|
|
577
|
+
if (input) {
|
|
578
|
+
input.setAttribute('data-chat-element', inputTypeAttr);
|
|
579
|
+
input.name = field;
|
|
580
|
+
input.value = valueStr;
|
|
581
|
+
input.checked = shouldBeChecked;
|
|
582
|
+
input.onclick = (e) => e.stopPropagation();
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const tickIcon = clone.querySelector('[data-chat-input-element="tick-icon"]');
|
|
586
|
+
if (tickIcon) {
|
|
587
|
+
tickIcon.style.display = shouldBeChecked ? 'block' : 'none';
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const textElement = clone.querySelector('[data-chat-input-element="text"]');
|
|
591
|
+
if (textElement) {
|
|
592
|
+
textElement.textContent = optionName;
|
|
593
|
+
textElement.style.display = '';
|
|
594
|
+
} else {
|
|
595
|
+
console.error('Text element not found in option');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
optionsWrapper.appendChild(clone);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
elements.messages.appendChild(optionsWrapper);
|
|
602
|
+
scrollToBottom();
|
|
603
|
+
|
|
604
|
+
const optionElements = optionsWrapper.querySelectorAll('[data-chat-input-element="container"]');
|
|
605
|
+
optionElements.forEach(el => {
|
|
606
|
+
el.onclick = null;
|
|
607
|
+
el.onclick = (e) => {
|
|
608
|
+
e.stopPropagation();
|
|
609
|
+
e.preventDefault();
|
|
610
|
+
handleOptionClick(el, field, isSingleSelect);
|
|
611
|
+
};
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// =============================================================================
|
|
616
|
+
// MULTI-SELECT-COLOR OPTIONS RENDERING
|
|
617
|
+
// =============================================================================
|
|
618
|
+
|
|
619
|
+
function renderColorOptions(options, field) {
|
|
620
|
+
if (!elements.messages) return;
|
|
621
|
+
|
|
622
|
+
const optionSelector = '[data-chat-element="multi-select-color"]';
|
|
623
|
+
const existingOption = document.querySelector(optionSelector);
|
|
624
|
+
|
|
625
|
+
if (!existingOption) {
|
|
626
|
+
console.error(`Element with ${optionSelector} not found in HTML. Please add it to your HTML.`);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const existingData = chatState.data[field];
|
|
631
|
+
console.log(`š Pre-filling ${field} (color):`, existingData);
|
|
632
|
+
|
|
633
|
+
const optionsWrapper = document.createElement('div');
|
|
634
|
+
optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
|
|
635
|
+
optionsWrapper.style.display = 'flex';
|
|
636
|
+
optionsWrapper.style.flexDirection = 'column';
|
|
637
|
+
optionsWrapper.style.alignItems = 'flex-start';
|
|
638
|
+
optionsWrapper.style.gap = '8px';
|
|
639
|
+
|
|
640
|
+
options.forEach((option, index) => {
|
|
641
|
+
const optionName = option.name || option;
|
|
642
|
+
const optionValue = option.value !== undefined ? option.value : option;
|
|
643
|
+
const optionColor = option.color || '#cccccc';
|
|
644
|
+
const valueStr = typeof optionValue === 'object' ?
|
|
645
|
+
JSON.stringify(optionValue) : String(optionValue);
|
|
646
|
+
|
|
647
|
+
const clone = existingOption.cloneNode(true);
|
|
648
|
+
clone.style.display = '';
|
|
649
|
+
|
|
650
|
+
const shouldBeChecked = Array.isArray(existingData) &&
|
|
651
|
+
existingData.some(v => JSON.stringify(v) === JSON.stringify(optionValue));
|
|
652
|
+
|
|
653
|
+
if (shouldBeChecked) {
|
|
654
|
+
clone.classList.add('cf-checked');
|
|
655
|
+
clone.style.backgroundColor = config.selectedBackground;
|
|
656
|
+
console.log(` ā
Pre-selected color: ${optionName}`);
|
|
657
|
+
} else {
|
|
658
|
+
clone.classList.remove('cf-checked');
|
|
659
|
+
clone.style.backgroundColor = 'transparent';
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
clone.setAttribute('data-chat-element', 'multi-select-color');
|
|
663
|
+
clone.setAttribute('data-field', field);
|
|
664
|
+
clone.setAttribute('data-value', valueStr);
|
|
665
|
+
clone.setAttribute('data-name', optionName);
|
|
666
|
+
clone.setAttribute('data-index', index);
|
|
667
|
+
clone.setAttribute('data-color', optionColor);
|
|
668
|
+
|
|
669
|
+
const input = clone.querySelector('[data-chat-input-element="input"]');
|
|
670
|
+
if (input) {
|
|
671
|
+
input.setAttribute('data-chat-element', 'multi-select-color');
|
|
672
|
+
input.name = field;
|
|
673
|
+
input.value = valueStr;
|
|
674
|
+
input.checked = shouldBeChecked;
|
|
675
|
+
input.onclick = (e) => e.stopPropagation();
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const tickIcon = clone.querySelector('[data-chat-input-element="tick-icon"]');
|
|
679
|
+
if (tickIcon) {
|
|
680
|
+
tickIcon.style.display = shouldBeChecked ? 'block' : 'none';
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const colorBlock = clone.querySelector('[data-chat-input-element="color-block"]');
|
|
684
|
+
if (colorBlock) {
|
|
685
|
+
colorBlock.style.backgroundColor = optionColor;
|
|
686
|
+
colorBlock.style.display = '';
|
|
687
|
+
} else {
|
|
688
|
+
console.warn('Color block element not found in multi-select-color template');
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const textElement = clone.querySelector('[data-chat-input-element="text"]');
|
|
692
|
+
if (textElement) {
|
|
693
|
+
textElement.textContent = optionName;
|
|
694
|
+
textElement.style.display = '';
|
|
695
|
+
} else {
|
|
696
|
+
console.error('Text element not found in option');
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
optionsWrapper.appendChild(clone);
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
elements.messages.appendChild(optionsWrapper);
|
|
703
|
+
scrollToBottom();
|
|
704
|
+
|
|
705
|
+
const optionElements = optionsWrapper.querySelectorAll('[data-chat-input-element="container"]');
|
|
706
|
+
optionElements.forEach(el => {
|
|
707
|
+
el.onclick = null;
|
|
708
|
+
el.onclick = (e) => {
|
|
709
|
+
e.stopPropagation();
|
|
710
|
+
e.preventDefault();
|
|
711
|
+
handleOptionClick(el, field, false);
|
|
712
|
+
};
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
|
|
715
716
|
// =============================================================================
|
|
716
717
|
// SINGLE-SELECT-CUSTOM (WITH MIN/MAX RANGE)
|
|
717
718
|
// =============================================================================
|
|
@@ -737,7 +738,6 @@ function renderCustomSelectOptions(options, field, customConfig) {
|
|
|
737
738
|
optionsWrapper.style.alignItems = 'flex-start';
|
|
738
739
|
optionsWrapper.style.gap = '8px';
|
|
739
740
|
|
|
740
|
-
// Render regular options
|
|
741
741
|
options.forEach((option, index) => {
|
|
742
742
|
const optionName = option.name || option;
|
|
743
743
|
const optionValue = option.value !== undefined ? option.value : option;
|
|
@@ -787,7 +787,6 @@ function renderCustomSelectOptions(options, field, customConfig) {
|
|
|
787
787
|
optionsWrapper.appendChild(clone);
|
|
788
788
|
});
|
|
789
789
|
|
|
790
|
-
// Render custom option
|
|
791
790
|
if (customConfig) {
|
|
792
791
|
const customClone = existingOption.cloneNode(true);
|
|
793
792
|
customClone.style.display = '';
|
|
@@ -1278,9 +1277,19 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
|
|
|
1278
1277
|
}
|
|
1279
1278
|
}
|
|
1280
1279
|
|
|
1281
|
-
|
|
1280
|
+
// ā
FIX: Handle pre-filled values for both validation and non-validation cases
|
|
1281
|
+
const hasValue = existingValue !== undefined && existingValue !== null && existingValue !== '';
|
|
1282
|
+
|
|
1283
|
+
if (hasValue) {
|
|
1282
1284
|
inputElement.value = existingValue;
|
|
1283
|
-
console.log(` ā
Pre-filled with: ${existingValue}`);
|
|
1285
|
+
console.log(` ā
Pre-filled with: "${existingValue}"`);
|
|
1286
|
+
|
|
1287
|
+
// Set currentSelection for pre-filled value
|
|
1288
|
+
chatState.currentSelection = {
|
|
1289
|
+
field,
|
|
1290
|
+
value: existingValue,
|
|
1291
|
+
name: existingValue.toString()
|
|
1292
|
+
};
|
|
1284
1293
|
|
|
1285
1294
|
if (hasValidation) {
|
|
1286
1295
|
const value = parseFloat(existingValue);
|
|
@@ -1293,14 +1302,30 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
|
|
|
1293
1302
|
|
|
1294
1303
|
if (isValid) {
|
|
1295
1304
|
enableNextButton();
|
|
1296
|
-
console.log(` ā
Pre-filled
|
|
1305
|
+
console.log(` ā
Pre-filled number is valid - Next button enabled`);
|
|
1297
1306
|
} else {
|
|
1298
1307
|
disableNextButton();
|
|
1299
|
-
console.log(` ā Pre-filled
|
|
1308
|
+
console.log(` ā Pre-filled number is invalid - Next button disabled`);
|
|
1300
1309
|
}
|
|
1310
|
+
} else {
|
|
1311
|
+
// ā
FIX: Enable button for pre-filled text input
|
|
1312
|
+
enableNextButton();
|
|
1313
|
+
console.log(` ā
Pre-filled text - Next button enabled`);
|
|
1301
1314
|
}
|
|
1302
1315
|
} else {
|
|
1316
|
+
// ā
No pre-filled value - set initial button state
|
|
1303
1317
|
inputElement.value = '';
|
|
1318
|
+
|
|
1319
|
+
const currentStep = flowData.flow[chatState.step];
|
|
1320
|
+
const inputRequired = currentStep.inputRequired === true;
|
|
1321
|
+
|
|
1322
|
+
if (inputRequired || hasValidation) {
|
|
1323
|
+
disableNextButton();
|
|
1324
|
+
console.log(` š Empty input (required or has validation) - Next button disabled`);
|
|
1325
|
+
} else {
|
|
1326
|
+
enableNextButton();
|
|
1327
|
+
console.log(` š Empty input (optional, no validation) - Next button enabled`);
|
|
1328
|
+
}
|
|
1304
1329
|
}
|
|
1305
1330
|
|
|
1306
1331
|
inputElement.type = inputType === 'number' ? 'number' : 'text';
|
|
@@ -1309,7 +1334,7 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
|
|
|
1309
1334
|
const value = inputType === 'number' ? parseFloat(e.target.value) : e.target.value;
|
|
1310
1335
|
|
|
1311
1336
|
chatState.data[field] = value;
|
|
1312
|
-
chatState.currentSelection = { field, value, name: value };
|
|
1337
|
+
chatState.currentSelection = { field, value, name: value.toString() };
|
|
1313
1338
|
|
|
1314
1339
|
if (hasValidation) {
|
|
1315
1340
|
const min = inputConfig.min;
|
|
@@ -1318,7 +1343,7 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
|
|
|
1318
1343
|
let isValid = true;
|
|
1319
1344
|
let errorMessage = '';
|
|
1320
1345
|
|
|
1321
|
-
if (value === '' || isNaN(value)) {
|
|
1346
|
+
if (e.target.value === '' || (inputType === 'number' && isNaN(value))) {
|
|
1322
1347
|
isValid = false;
|
|
1323
1348
|
errorMessage = 'Please enter a valid number';
|
|
1324
1349
|
} else if (min !== undefined && value < min) {
|
|
@@ -1331,13 +1356,29 @@ function renderTextInput(field, inputType = 'text', inputConfig = {}) {
|
|
|
1331
1356
|
|
|
1332
1357
|
if (isValid) {
|
|
1333
1358
|
enableNextButton();
|
|
1334
|
-
console.log(` ā
Number input valid: ${value}`);
|
|
1359
|
+
console.log(` ā
Number input valid: ${value} - Next button enabled`);
|
|
1335
1360
|
} else {
|
|
1336
1361
|
disableNextButton();
|
|
1337
|
-
console.log(` ā Number input invalid: ${errorMessage}`);
|
|
1362
|
+
console.log(` ā Number input invalid: ${errorMessage} - Next button disabled`);
|
|
1338
1363
|
}
|
|
1339
1364
|
} else {
|
|
1340
|
-
|
|
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`);
|
|
1377
|
+
} else {
|
|
1378
|
+
enableNextButton();
|
|
1379
|
+
console.log(` š Text input empty but not required - Next button enabled`);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1341
1382
|
}
|
|
1342
1383
|
};
|
|
1343
1384
|
}
|
|
@@ -1476,7 +1517,6 @@ async function handleNext() {
|
|
|
1476
1517
|
return;
|
|
1477
1518
|
}
|
|
1478
1519
|
|
|
1479
|
-
// VALIDATION: Check for single-select-custom validation before proceeding
|
|
1480
1520
|
if (currentStep.inputType === 'single-select-custom' && currentStep.input) {
|
|
1481
1521
|
const field = currentStep.input.field;
|
|
1482
1522
|
const customConfig = currentStep.input.custom;
|
|
@@ -1659,18 +1699,8 @@ async function showNextStep() {
|
|
|
1659
1699
|
const inputType = nextStep.inputType || 'single-select';
|
|
1660
1700
|
|
|
1661
1701
|
if (inputType === 'text' || inputType === 'number') {
|
|
1702
|
+
// ā
renderTextInput handles ALL button state logic (pre-filled, typing, validation)
|
|
1662
1703
|
renderTextInput(nextStep.input.field, inputType, nextStep.input);
|
|
1663
|
-
|
|
1664
|
-
const hasValidation = inputType === 'number' &&
|
|
1665
|
-
(nextStep.input.min !== undefined || nextStep.input.max !== undefined);
|
|
1666
|
-
|
|
1667
|
-
if (inputRequired || hasValidation) {
|
|
1668
|
-
disableNextButton();
|
|
1669
|
-
console.log(' š Next button disabled initially (input required or validation enabled)');
|
|
1670
|
-
} else {
|
|
1671
|
-
enableNextButton();
|
|
1672
|
-
console.log(' š Next button enabled (optional input, no validation)');
|
|
1673
|
-
}
|
|
1674
1704
|
} else if (inputType === 'multi-select-color') {
|
|
1675
1705
|
renderColorOptions(nextStep.input.options, nextStep.input.field);
|
|
1676
1706
|
|
|
@@ -1690,14 +1720,16 @@ async function showNextStep() {
|
|
|
1690
1720
|
enableNextButton();
|
|
1691
1721
|
}
|
|
1692
1722
|
} else if (inputType === 'multi-select-dropdown') {
|
|
1693
|
-
// Render multi-select dropdown
|
|
1723
|
+
// ā
Render multi-select dropdown
|
|
1694
1724
|
renderMultiSelectDropdown(nextStep.input.options, nextStep.input.field);
|
|
1695
1725
|
|
|
1696
|
-
//
|
|
1726
|
+
// ā
FIX: Enable Next button initially if NOT required
|
|
1697
1727
|
if (inputRequired) {
|
|
1698
1728
|
disableNextButton();
|
|
1729
|
+
console.log(' š Next button disabled initially (input required)');
|
|
1699
1730
|
} else {
|
|
1700
1731
|
enableNextButton();
|
|
1732
|
+
console.log(' š Next button enabled (input NOT required)');
|
|
1701
1733
|
}
|
|
1702
1734
|
} else {
|
|
1703
1735
|
const isSingleSelect = inputType === 'single-select';
|