Bugs | AI Chatbot Widget

Hi there!

Yesterday, I installed the AI Chatbot widget on our site. So far, our team is loving it.

During the course of using your AI Chatbot widget, I discovered the following bugs:

  1. Form field heights do not match. Specifically, the Email and Phone fields are larger than the Name field. Details: Screenshot by Lightshot . I recommend they all be the same (dimensionally).
  2. Phone field display (frontend) does not match Phone field display (test panel). Details (test panel, looks good): Screenshot by Lightshot Details (frontend, does not look good): Screenshot by Lightshot Fix appreciated.
  3. Widget does not validate Email and Phone entries. I used several fake emails and phone numbers. The form was processed anyway. Please incorporate both an Email and Phone validator.
  4. Form submits successfully when only Phone field is filled in. This does not help us all. In order for the form to process sucessfully – as a minimum – both the Name and Email (or Phone) fields need to be filled. Please update your form’s logic.

For now, that’s it! If I find other bugs, I’ll let you know. :slight_smile:

Thank you!

1 Like

Hi @AeroConsultants,

Thank you for your kind words about our AI Chatbot! We appreciate your sharp eye, and we’ll take everything you said into consideration to continue to better ourselves :hugs:

Now, let’s address all your points one-by-one:

Form field heights do not match + Phone field display
We looked into the first two issues, and it appears to be a style conflict between our widget and your CMS. The dev team fixed it up for you using this CSS code:

input {
height: auto !important;
}

Please check if everything’s fixed now and let me know :slight_smile:

Widget does not validate Email and Phone entries
I wanted to let you know that right now our AI Chatbot app doesn’t support email or phone number validation. However, I added a feature request on your behalf for this important functionality: Add email and phone number validation.

Please feel free to add any further clarifications to the topic and upvote it to stay updated :raising_hands:

Form submits successfully when only Phone field is filled in
I absolutely get why this functionality is important for your case! While a feature request for it is still gathering votes, I’ll see if we can customise the form for you with the help of our developers. Please bear with me for some time, and I’ll get back to you with the news soon.

Drop a vote for the Wishlist topic in the meantime :wink:

Stay tuned, and feel free to ask any questions you have further!

1 Like

Hi @Irene,

You are awesome. Thank you.

The CSS rule you provided fixed our issue. Since the issue is caused by our CMS (i.e., WordPress), perhaps your developers can integrate their solution into your widget’s baseline code. It would help keep subsequent “bug” reports down to a minimum — perhaps zero!

Concerning the rest, so grateful! I upvoted the Wishlist topic you posted on our behalf.

Again, thank you.

3 Likes

Thank you so much for your kind words! It makes me happy to know that I helped you successfully :hugs:

Let’s continue our customisation journey! The devs came through with the script to make all the AI Chatbot form fields required while we work on a built-in solution:

const WIDGET_ID = '9019baa4-6ea8-4e64-9856-5eab9dc16b55';
const EMPTY_ERROR_TEXT = 'This field is required';

const ROOT_SELECTOR = `.eapps-ai-chatbot-${WIDGET_ID}-custom-css-root`;
const NAME_INPUT_SELECTOR = `${ROOT_SELECTOR} input[id="name"]`;
const EMAIL_INPUT_SELECTOR = `${ROOT_SELECTOR} input[id="email"]`;
const PHONE_INPUT_SELECTOR = `${ROOT_SELECTOR} input[id="phone"]`;
const FORM_SELECTOR = `${ROOT_SELECTOR} form`;
const SUBMIT_BUTTON_SELECTOR = 'button[type="submit"]';
const PLACEHOLDER_SELECTOR =
	'[class*="TextControlBase__TextControlBasePlaceholder-sc"]';

const waitForElement = (selector, root = document) =>
	new Promise((resolve) => {
		const existing = root.querySelector(selector);
		if (existing) return resolve(existing);

		const observer = new MutationObserver(() => {
			const el = root.querySelector(selector);
			if (el) {
				observer.disconnect();
				resolve(el);
			}
		});
		observer.observe(root, { childList: true, subtree: true });
	});

const createErrorMessage = (text = EMPTY_ERROR_TEXT) => {
	const el = document.createElement('div');
	Object.assign(el.style, {
		color: 'rgb(255, 56, 56)',
		fontSize: '12px',
		lineHeight: '14px',
		marginTop: '8px',
	});
	el.classList.add('es-custom-error-message');
	el.textContent = text;
	return el;
};

const getFieldContainer = (inputEl) =>
	inputEl.closest('[class*="FormFieldLayout__Element-sc"]') ||
	inputEl.parentNode;

const showErrorFor = (inputEl) => {
	const container = getFieldContainer(inputEl);
	if (!container) return;

	if (!container.querySelector('.es-custom-error-message')) {
		container.appendChild(createErrorMessage());
	}

	inputEl.style.borderColor = 'rgb(255, 56, 56)';
};

const hideErrorFor = (inputEl) => {
	const container = getFieldContainer(inputEl);
	if (!container) return;

	container.querySelector('.es-custom-error-message')?.remove();
	inputEl.style.borderColor = '';
};

const initWatcher = async () => {
	const emailField = await waitForElement(EMAIL_INPUT_SELECTOR);
	const nameField = await waitForElement(NAME_INPUT_SELECTOR);
	const phoneField = await waitForElement(PHONE_INPUT_SELECTOR);

	let form = emailField.closest('form');
	if (!form) {
		form = await waitForElement(FORM_SELECTOR);
		return initWatcher();
	}

	const button = await waitForElement(SUBMIT_BUTTON_SELECTOR, form);

	const changePlaceholder = () => {
		setTimeout(() => {
			const emailPlaceholder =
				emailField.parentNode?.querySelector(PLACEHOLDER_SELECTOR);
			const namePlaceholder =
				nameField.parentNode?.querySelector(PLACEHOLDER_SELECTOR);
			const phonePlaceholder =
				phoneField.parentNode?.querySelector(PLACEHOLDER_SELECTOR);

			if (
				emailPlaceholder &&
				!emailPlaceholder.textContent.trim().endsWith('*')
			) {
				emailPlaceholder.textContent = `${emailPlaceholder.textContent.trim()} *`;
			}
			if (
				namePlaceholder &&
				!namePlaceholder.textContent.trim().endsWith('*')
			) {
				namePlaceholder.textContent = `${namePlaceholder.textContent.trim()} *`;
			}
			if (
				phonePlaceholder &&
				!phonePlaceholder.textContent.trim().endsWith('*')
			) {
				phonePlaceholder.textContent = `${phonePlaceholder.textContent.trim()} *`;
			}
		}, 50);
	};

	const updateButtonState = () => {
		const empty =
			!emailField.value.trim() ||
			!nameField.value.trim() ||
			!phoneField.value.trim();

		button.style.opacity = empty ? '0.5' : '1';
		button.style.cursor = empty ? 'default' : 'pointer';

		if (empty) {
			button.onclick = (e) => {
				e.preventDefault();

				if (!emailField.value.trim()) showErrorFor(emailField);
				if (!nameField.value.trim()) showErrorFor(nameField);
				if (!phoneField.value.trim()) showErrorFor(phoneField);
			};
		} else {
			button.onclick = null;
		}
	};

	const handleInput = () => {
		changePlaceholder();
		updateButtonState();

		// field-level validation
		if (!emailField.value.trim()) showErrorFor(emailField);
		else hideErrorFor(emailField);

		if (!nameField.value.trim()) showErrorFor(nameField);
		else hideErrorFor(nameField);

		if (!phoneField.value.trim()) showErrorFor(phoneField);
		else hideErrorFor(phoneField);
	};

	emailField.addEventListener('input', handleInput);
	nameField.addEventListener('input', handleInput);
	phoneField.addEventListener('input', handleInput);

	form.addEventListener('click', (e) => {
		if (e.target === button) {
			if (!emailField.value.trim()) {
				e.preventDefault();
				showErrorFor(emailField);
			}
			if (!nameField.value.trim()) {
				e.preventDefault();
				showErrorFor(nameField);
			}
			if (!phoneField.value.trim()) {
				e.preventDefault();
				showErrorFor(phoneField);
			}
		}
	});

	changePlaceholder();
	updateButtonState();

	const chatbotRoot = document.querySelector(ROOT_SELECTOR);
	if (chatbotRoot) {
		const observer = new MutationObserver(() => {
			if (!chatbotRoot.contains(emailField)) {
				observer.disconnect();
				initWatcher();
			}
		});
		observer.observe(chatbotRoot, { childList: true, subtree: true });
	}
};

initWatcher();

Please add it to the Custom JS section of your widget editor. But note that Custom JS won’t function in the widget editor/preview – only on the live site or sharelink.

Try it out and share your thoughts :raising_hands:

3 Likes

Happy Wednesday, @Irene!

Hot dog! Worked flawlessly. Details:

This will benefit all of your customers by minimizing spam or time spent replying to obviously-fake emails.

Thank you!

Petar

2 Likes