class EveAIChatWidget extends HTMLElement { static get observedAttributes() { return ['tenant-id', 'api-key', 'domain', 'language']; } constructor() { super(); this.socket = null; // Initialize socket to null this.attributesSet = false; // Flag to check if all attributes are set this.jwtToken = null; // Initialize jwtToken to null console.log('EveAIChatWidget constructor called'); } connectedCallback() { console.log('connectedCallback called'); this.innerHTML = this.getTemplate(); this.messagesArea = this.querySelector('.messages-area'); this.questionInput = this.querySelector('.question-area input'); this.statusLine = this.querySelector('.status-line'); this.querySelector('.question-area button').addEventListener('click', () => this.handleSendMessage()); this.questionInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { this.handleSendMessage(); } }); if (this.areAllAttributesSet() && !this.socket) { console.log('Attributes already set in connectedCallback, initializing socket'); this.initializeSocket(); } } attributeChangedCallback(name, oldValue, newValue) { console.log(`attributeChangedCallback called: ${name} changed from ${oldValue} to ${newValue}`); this.updateAttributes(); console.log('Current attributes:', { tenantId: this.getAttribute('tenant-id'), apiKey: this.getAttribute('api-key'), domain: this.getAttribute('domain'), language: this.getAttribute('language') }); if (this.areAllAttributesSet() && !this.socket) { console.log('All attributes set in attributeChangedCallback, initializing socket'); this.attributesSet = true; this.initializeSocket(); } } updateAttributes() { console.log('Updating attributes:'); this.tenantId = this.getAttribute('tenant-id'); this.apiKey = this.getAttribute('api-key'); this.domain = this.getAttribute('domain'); this.language = this.getAttribute('language'); console.log('Updated attributes:', { tenantId: this.tenantId, apiKey: this.apiKey, domain: this.domain, language: this.language }); } areAllAttributesSet() { const tenantId = this.getAttribute('tenant-id'); const apiKey = this.getAttribute('api-key'); const domain = this.getAttribute('domain'); const language = this.getAttribute('language'); console.log('Checking if all attributes are set:', { tenantId, apiKey, domain, language }); return tenantId && apiKey && domain; } initializeSocket() { if (this.socket) { console.log('Socket already initialized'); return; } if (!this.domain || this.domain === 'null') { console.error('Domain attribute is missing or invalid'); this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.') return; } console.log(`Initializing socket connection to ${this.domain}`); // Ensure apiKey is passed in the query parameters this.socket = io(this.domain, { path: '/chat/socket.io/', transports: ['websocket', 'polling'], query: { tenantId: this.tenantId, apiKey: this.apiKey // Ensure apiKey is included here }, auth: { token: 'Bearer ' + this.apiKey // Ensure token is included here } }); this.socket.on('connect', (data) => { console.log('Socket connected'); this.setStatusMessage('Connected to EveAI.') }); this.socket.on('authenticated', (data) => { console.log('Authenticated event received: ', data); this.setStatusMessage('Authenticated.') if (data.token) { this.jwtToken = data.token; // Store the JWT token received from the server } }); this.socket.on('connect_error', (err) => { console.error('Socket connection error:', err); this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.') }); this.socket.on('connect_timeout', () => { console.error('Socket connection timeout'); this.setStatusMessage('EveAI Chat Widget needs further configuration by site administrator.') }); this.socket.on('disconnect', () => { console.log('Socket disconnected'); this.setStatusMessage('Disconnected from EveAI. Please refresh the page for further interaction.') }); this.socket.on('bot_response', (data) => { if (data.tenantId === this.tenantId) { console.log('Initial response received:', data) console.log('Task ID received:', data.taskId) this.checkTaskStatus(data.taskId) this.setStatusMessage('Processing...') } }); this.socket.on('task_status', (data) => { console.log('Task status received:', data.status) console.log('Task ID received:', data.taskId) console.log('Citations type:', typeof data.citations, 'Citations:', data.citations); if (data.status === 'pending') { this.updateProgress(); setTimeout(() => this.checkTaskStatus(data.taskId), 1000); // Poll every second } else if (data.status === 'success') { this.addBotMessage(data.answer, data.interaction_id, data.algorithm, data.citations); this.clearProgress(); // Clear progress indicator when done } else { this.setStatusMessage('Failed to process message.'); } }); } setStatusMessage(message) { this.statusLine.textContent = message; } updateProgress() { if (!this.statusLine.textContent) { this.statusLine.textContent = 'Processing...'; } else { this.statusLine.textContent += '.'; // Append a dot } } clearProgress() { this.statusLine.textContent = ''; } checkTaskStatus(taskId) { this.updateProgress(); this.socket.emit('check_task_status', { task_id: taskId }); } getTemplate() { return `
${text}
`; this.messagesArea.appendChild(message); this.messagesArea.scrollTop = this.messagesArea.scrollHeight; } addBotMessage(text, interactionId, algorithm = 'default', citations = []) { const message = document.createElement('div'); message.classList.add('message', 'bot'); let content = marked.parse(text); // Use marked to convert markdown to HTML // Ensure citations is an array if (!Array.isArray(citations)) { console.error('Expected citations to be an array, but got:', citations); citations = []; // Default to an empty array } let citationsHtml = citations.map(url => `${url}`).join('${content}
${citationsHtml ? `${citationsHtml}
` : ''} `; this.messagesArea.appendChild(message); this.messagesArea.scrollTop = this.messagesArea.scrollHeight; } handleSendMessage() { console.log('handleSendMessage called'); const message = this.questionInput.value.trim(); if (message) { this.addUserMessage(message); this.questionInput.value = ''; this.sendMessageToBackend(message); } } sendMessageToBackend(message) { console.log('sendMessageToBackend called'); if (!this.socket) { console.error('Socket is not initialized'); return; } if (!this.jwtToken) { console.error('JWT token is not available'); return; } console.log('Sending message to backend'); this.socket.emit('user_message', { tenantId: this.tenantId, token: this.jwtToken, message, language: this.language }); this.setStatusMessage('Processing started ...') } } customElements.define('eveai-chat-widget', EveAIChatWidget); function handleFeedback(feedback, interactionId) { // Send feedback to the backend console.log(`Feedback for ${interactionId}: ${feedback}`); this.socket.emit('feedback', { feedback, interaction_id: interactionId }); }