const chatMessages = document.getElementById('chat-messages');
const chatInput = document.getElementById('chat-input');
const sendButton = document.getElementById('send-button');
const errorMessage = document.getElementById('error-message');
const headerTitle = document.getElementById('chat-header-title');
const headerSubtitle = document.getElementById('chat-header-subtitle');
const closeButton = document.getElementById('close-button');
const loadingPlaceholder = document.getElementById('loading-placeholder');
const suggestionsContainer = document.getElementById('suggestions-container');
let API_WEBHOOK_URL = 'https://agent.vynex.org/webhook/2174d0c4-093c-4835-9f89-825a4a345319';
let INITIAL_MESSAGE = 'Hello! How can I help you?';
let HEADER_TITLE = 'Chat Assistant';
let SUBTITLE = 'Brilliant Directories AI Assistant';
let USER_ID = { staticId: 'unknown-widget-user' };
let ACCENT_COLOR = '#1a73e8';
let USER_MSG_BG = '#e1f5fe';
let BOT_MSG_BG = '#f1f1f1';
let API_REQUEST_MESSAGE_KEY = 'message';
let isLoading = false;
let loadingIndicatorElement = null;
function getQueryParam(param) {
const urlParams = new URLSearchParams(window.location.search);
const value = urlParams.get(param);
return value ? decodeURIComponent(value) : null;
}
function applyConfiguration() {
const configWebhookUrl = getQueryParam('webhookUrl');
const configInitialMessage = getQueryParam('initialMessage');
const configTitle = getQueryParam('title');
const configSubtitle = getQueryParam('subtitle');
const configUserId = getQueryParam('userId');
const configAccent = getQueryParam('accentColor');
const configAccentSecondary = getQueryParam('accentColorSecondary');
const configUserBg = getQueryParam('userMsgBg');
const configAgentBg = getQueryParam('agentMsgBg');
if (configWebhookUrl) API_WEBHOOK_URL = configWebhookUrl;
if (configInitialMessage) INITIAL_MESSAGE = configInitialMessage;
if (configTitle) HEADER_TITLE = configTitle;
if (configSubtitle) SUBTITLE = configSubtitle;
if (configAccent) document.documentElement.style.setProperty('--accent-color', configAccent);
if (configAccentSecondary) document.documentElement.style.setProperty('--accent-color-secondary', configAccentSecondary);
if (configUserBg) document.documentElement.style.setProperty('--user-msg-bg', configUserBg);
if (configAgentBg) document.documentElement.style.setProperty('--agent-msg-bg', configAgentBg);
if (configUserId) {
try {
USER_ID = JSON.parse(configUserId);
if (typeof USER_ID !== 'object' || USER_ID === null) {
console.warn("Received userId parameter is not a valid JSON object, using default.");
USER_ID = { staticId: 'unknown-widget-user' };
}
} catch (e) {
console.error("Failed to parse userId parameter, using default.", e);
USER_ID = { staticId: 'error-parsing-user-id' };
}
}
headerTitle.textContent = HEADER_TITLE;
headerSubtitle.textContent = SUBTITLE;
}
function displayMessage(text, sender) {
hideLoading();
const messageDiv = document.createElement('div');
messageDiv.classList.add('message', `${sender}-message`);
const searchResultsDiv = document.querySelector(".grid-container");
if (sender === 'agent') {
try {
marked.setOptions({ breaks: true, gfm: true });
const messageText = typeof text === 'object' ? text.output : text;
const rawHtml = marked.parse(messageText || "");
const sanitizedHtml = DOMPurify.sanitize(rawHtml);
messageDiv.innerHTML = sanitizedHtml;
if (typeof text === 'object' && text !== null) {
if(text.posts && Array.isArray(text.posts)|| text.members && Array.isArray(text.members) ) {
searchResultsDiv.innerHTML = "";
document.querySelectorAll(
".member-search-result-count-container, .member_results_header, .member-search-result-filters, .member_results_header ~ hr, .member_results_header ~ br, .member-pagination-block, .module"
).forEach(el => el.style.display = "none");
}
if (text.members && Array.isArray(text.members)) {
const membersContainer = document.createElement('div');
membersContainer.classList.add('chat-widget-container');
const membersList = document.createElement('ul');
membersList.classList.add('chat-widget-list');
text.members.forEach(member => {
const memberItem = document.createElement('li');
memberItem.classList.add('chat-widget-item');
console.log(member);
const profilePhoto = member.photo ?
`https://www.talkinghealthtech.com/logos/profile/${member.photo}` :
'https://placehold.co/36x36/6a82fb/ffffff?text=' + member.name.charAt(0);
const location = [member.city, member.state_ln]
.filter(Boolean)
.join(', ');
const services = member.services.slice(0, 3).join(', ');
memberItem.innerHTML = `
${location}
${Array.isArray(member.services) && member.services.length
? member.services.slice(0, 3).map(service =>
`${service}`
).join('')
: 'No services'
}
${member.about_me}
`;
membersList.appendChild(memberItem);
});
membersContainer.appendChild(membersList);
searchResultsDiv.appendChild(membersContainer);
}
if (text.posts && Array.isArray(text.posts)) {
const postsContainer = document.createElement('div');
postsContainer.classList.add('chat-widget-container');
const postsList = document.createElement('ul');
postsList.classList.add('chat-widget-list');
text.posts.forEach(post => {
const postItem = document.createElement('li');
postItem.classList.add('chat-widget-item');
const thumbnail = post.image
? `https://www.talkinghealthtech.com${post.image}`
: `https://placehold.co/70x50/17a2b8/ffffff?text=${encodeURIComponent(post.category || 'Post')}`;
const location = post.location || 'Online';
let dateStr = '';
if (post.post_live_date) {
try {
const date = new Date(post.post_live_date);
if (!isNaN(date.getTime())) {
dateStr = date.toLocaleString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
} else {
dateStr = post.post_live_date;
}
} catch (e) {
dateStr = post.post_live_date;
}
}
const tags = post.post_tags ? post.post_tags.split(',').map(tag => tag.trim()) : [];
postItem.innerHTML = `
By ${post.author || 'Unknown'}
${post.title || ''}
${post.category || ''}
${location}
${post.description ? post.description.substring(0, 120) + (post.description.length > 120 ? '...' : '') : ''}
${tags.length > 0 ? `
${tags.map(tag => `${tag}`).join('')}
` : ''}
`;
postsList.appendChild(postItem);
});
postsContainer.appendChild(postsList);
searchResultsDiv.appendChild(postsContainer);
}
}
} catch (error) {
console.error("Error processing agent message:", error);
messageDiv.textContent = typeof text === 'object' ? text.output : text;
}
} else {
messageDiv.textContent = text;
}
chatMessages.appendChild(messageDiv);
scrollToBottom();
}
function scrollToBottom() {
chatMessages.scrollTop = chatMessages.scrollHeight;
}
function showLoading() {
if (isLoading) return;
isLoading = true;
updateSendButtonState();
if (!loadingIndicatorElement) {
loadingIndicatorElement = document.createElement('div');
loadingIndicatorElement.classList.add('loading-indicator');
loadingIndicatorElement.innerHTML = ``;
}
chatMessages.appendChild(loadingIndicatorElement);
scrollToBottom();
}
function hideLoading() {
if (!isLoading) return;
isLoading = false;
if (loadingIndicatorElement && loadingIndicatorElement.parentNode) {
loadingIndicatorElement.parentNode.removeChild(loadingIndicatorElement);
}
updateSendButtonState();
}
function showAgentError(message) {
if (message) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
} else {
errorMessage.style.display = 'none';
}
scrollToBottom();
}
function autoResizeTextarea() {
const textarea = chatInput;
textarea.style.height = '48px';
const newHeight = Math.min(Math.max(textarea.scrollHeight, 48), 200);
textarea.style.height = newHeight + 'px';
}
function updateSendButtonState() {
const hasText = chatInput.value.trim().length > 0;
sendButton.disabled = !hasText || isLoading;
}
chatInput.addEventListener('input', () => {
autoResizeTextarea();
updateSendButtonState();
});
chatInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
if (!sendButton.disabled) {
sendMessageToApi();
}
}
});
sendButton.addEventListener('click', () => {
if (!sendButton.disabled) {
sendMessageToApi();
}
});
function handleSuggestionClick(event) {
if (event.target.classList.contains('suggestion-bubble')) {
const suggestionText = event.target.textContent;
chatInput.value = suggestionText;
sendMessageToApi();
suggestionsContainer.style.display = 'none';
}
}
suggestionsContainer.addEventListener('click', handleSuggestionClick);
const originalSendMessage = sendMessageToApi;
sendMessageToApi = function() {
if (chatInput.value.trim() === '') return;
originalSendMessage();
suggestionsContainer.style.display = 'none';
chatInput.style.height = '48px';
chatInput.value = '';
updateSendButtonState();
};
document.addEventListener('DOMContentLoaded', () => {
applyConfiguration();
if (INITIAL_MESSAGE) {
displayMessage(INITIAL_MESSAGE, 'agent');
}
scrollToBottom();
updateSendButtonState();
chatInput.focus();
suggestionsContainer.style.display = 'flex';
});
chatInput.addEventListener('input', () => {
if (chatInput.value === '') {
suggestionsContainer.style.display = 'flex';
}
});
async function sendMessageToApi() {
const userMessage = chatInput.value.trim();
if (!userMessage || isLoading) return;
displayMessage(userMessage, 'user');
chatInput.value = '';
updateSendButtonState();
showAgentError(null);
showLoading();
try {
const requestData = {
[API_REQUEST_MESSAGE_KEY]: userMessage,
user: USER_ID
};
const response = await fetch(API_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestData)
});
if (!response.ok) {
let errorDetails = `HTTP error! Status: ${response.status}`;
try { const errorData = await response.json(); errorDetails += ` - ${errorData.message || JSON.stringify(errorData)}`; } catch (e) { /* Ignore */ }
throw new Error(errorDetails);
}
const data = await response.json();
const API_RESPONSE_MESSAGE_KEY = 'output';
const agentReply = data[API_RESPONSE_MESSAGE_KEY];
if (typeof agentReply === 'string' && agentReply.length > 0) {
const messageData = {
output: agentReply,
members: data.members,
posts: data.posts
};
displayMessage(messageData, 'agent');
} else if (agentReply) {
displayMessage(String(agentReply), 'agent');
console.warn('Received agent reply, but not a non-empty string.', {response: data});
} else {
console.warn('Received response, but expected key missing/empty.', {key: API_RESPONSE_MESSAGE_KEY, response: data});
displayMessage("Sorry, I didn't get a valid response structure.", 'agent');
}
} catch (error) {
console.error('Error sending/receiving message:', error);
hideLoading();
showAgentError(`Error: ${error.message || 'Could not reach assistant.'}`);
} finally {
chatInput.focus();
updateSendButtonState();
}
}
sendButton.addEventListener('click', sendMessageToApi);
chatInput.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
if (!sendButton.disabled) {
sendMessageToApi();
}
}
});
closeButton.addEventListener('click', () => {
window.parent.postMessage('closeChatWidget', '*');
});