Files
eveAI/nginx/frontend_src/js/chat-client.js

338 lines
12 KiB
JavaScript

// CSS imports
import '../css/chat-client.css';
import '../../../eveai_chat_client/static/assets/css/chat.css';
import '../../../eveai_chat_client/static/assets/css/chat-components.css';
import '../../../eveai_chat_client/static/assets/css/chat-input.css';
import '../../../eveai_chat_client/static/assets/css/chat-message.css';
import '../../../eveai_chat_client/static/assets/css/form.css';
import '../../../eveai_chat_client/static/assets/css/form-message.css';
// Dependencies
import { createApp, version } from 'vue';
import { marked } from 'marked';
// Import LanguageProvider for sidebar translation support
import { createLanguageProvider, LANGUAGE_PROVIDER_KEY } from '../../../eveai_chat_client/static/assets/js/services/LanguageProvider.js';
// Vue en andere bibliotheken beschikbaar maken
window.Vue = { createApp, version };
window.marked = marked;
// Gebruik barrel export voor componenten
import * as Components from '../../../eveai_chat_client/static/assets/vue-components/index.js';
// Maak Components globaal beschikbaar voor debugging
window.Components = Components;
console.log('Components loaded:', Object.keys(Components));
// Import specifieke componenten
import LanguageSelector from '../../../eveai_chat_client/static/assets/vue-components/LanguageSelector.vue';
import ChatApp from '../../../eveai_chat_client/static/assets/vue-components/ChatApp.vue';
import ChatRoot from '../../../eveai_chat_client/static/assets/vue-components/ChatRoot.vue';
import SideBar from '../../../eveai_chat_client/static/assets/vue-components/SideBar.vue';
import MobileHeader from '../../../eveai_chat_client/static/assets/vue-components/MobileHeader.vue';
// VueUse-setup voor de chatclient (maakt composables beschikbaar via window.VueUse)
import './vueuse-setup.js';
// Globale Vue error tracking
window.addEventListener('error', function(event) {
console.error('🚨 [Global Error]', event.error);
});
// Wacht tot DOM geladen is
document.addEventListener('DOMContentLoaded', function() {
// Controleer of chatConfig is ingesteld
if (!window.chatConfig) {
console.error('chatConfig is niet beschikbaar');
window.chatConfig = {};
}
// Initialiseer sidebar (vervangt fillSidebarExplanation en initializeLanguageSelector)
initializeSidebar();
// Initialiseer mobile header
initializeMobileHeader();
// Initialiseer chat app (simpel)
initializeChatApp();
});
/**
* Initialiseert de sidebar component
*/
function initializeSidebar() {
const container = document.getElementById('sidebar-container');
if (!container) {
console.error('#sidebar-container niet gevonden');
return;
}
try {
// Maak props voor de component
const props = {
tenantMake: {
name: window.chatConfig.tenantMake?.name || '',
logo_url: window.chatConfig.tenantMake?.logo_url || '',
subtitle: window.chatConfig.tenantMake?.subtitle || ''
},
explanationText: window.chatConfig.explanation || '',
initialLanguage: window.chatConfig.language || 'nl',
supportedLanguageDetails: window.chatConfig.supportedLanguageDetails || {},
allowedLanguages: window.chatConfig.allowedLanguages || ['nl', 'en', 'fr', 'de'],
apiPrefix: window.chatConfig.apiPrefix || ''
};
// Mount de component
const app = createApp(SideBar, props);
// Create and provide LanguageProvider for sidebar components
const initialLanguage = window.chatConfig?.language || 'nl';
const apiPrefix = window.chatConfig?.apiPrefix || '';
const languageProvider = createLanguageProvider(initialLanguage, apiPrefix);
app.provide(LANGUAGE_PROVIDER_KEY, languageProvider);
// Error handler
app.config.errorHandler = (err, vm, info) => {
console.error('🚨 [Vue Error in Sidebar]', err);
console.error('Component:', vm);
console.error('Error Info:', info);
};
const mountedApp = app.mount(container);
// Listen to language change events and update the sidebar's language provider
const languageChangeHandler = (event) => {
if (event.detail && event.detail.language) {
console.log('Sidebar: Received language change event:', event.detail.language);
languageProvider.setLanguage(event.detail.language);
}
};
document.addEventListener('language-changed', languageChangeHandler);
// Store the handler for cleanup if needed
mountedApp._languageChangeHandler = languageChangeHandler;
console.log('✅ Sidebar component successfully mounted with LanguageProvider');
return mountedApp;
} catch (error) {
console.error('🚨 [CRITICAL ERROR] Bij initialiseren sidebar:', error);
}
}
/**
* Initialiseert de mobile header component
*/
function initializeMobileHeader() {
const container = document.getElementById('mobile-header-container');
if (!container) {
console.error('#mobile-header-container niet gevonden');
return;
}
try {
// Maak props voor de component
const props = {
tenantMake: {
name: window.chatConfig.tenantMake?.name || '',
logo_url: window.chatConfig.tenantMake?.logo_url || '',
subtitle: window.chatConfig.tenantMake?.subtitle || ''
},
initialLanguage: window.chatConfig.language || 'nl',
supportedLanguageDetails: window.chatConfig.supportedLanguageDetails || {},
allowedLanguages: window.chatConfig.allowedLanguages || ['nl', 'en', 'fr', 'de'],
apiPrefix: window.chatConfig.apiPrefix || ''
};
// Mount de component
const app = createApp(MobileHeader, props);
// Create and provide LanguageProvider for mobile header components
const initialLanguage = window.chatConfig?.language || 'nl';
const apiPrefix = window.chatConfig?.apiPrefix || '';
const languageProvider = createLanguageProvider(initialLanguage, apiPrefix);
app.provide(LANGUAGE_PROVIDER_KEY, languageProvider);
// Error handler
app.config.errorHandler = (err, vm, info) => {
console.error('🚨 [Vue Error in MobileHeader]', err);
console.error('Component:', vm);
console.error('Error Info:', info);
};
const mountedApp = app.mount(container);
// Dynamisch de headerhoogte doorgeven aan CSS
const updateHeaderHeightVar = () => {
const isMobile = window.matchMedia('(max-width: 768px)').matches;
if (!isMobile) {
document.documentElement.style.removeProperty('--mobile-header-height');
return;
}
const h = container.offsetHeight || 60; // fallback
document.documentElement.style.setProperty('--mobile-header-height', `${h}px`);
};
// Initieel instellen en bij gebeurtenissen herberekenen
requestAnimationFrame(updateHeaderHeightVar);
window.addEventListener('resize', updateHeaderHeightVar);
// Listen to language change events and update the mobile header's language provider
const languageChangeHandler = (event) => {
if (event.detail && event.detail.language) {
console.log('MobileHeader: Received language change event:', event.detail.language);
languageProvider.setLanguage(event.detail.language);
// taalwissel kan headerhoogte veranderen
requestAnimationFrame(updateHeaderHeightVar);
}
};
document.addEventListener('language-changed', languageChangeHandler);
// Store the handler for cleanup if needed
mountedApp._languageChangeHandler = languageChangeHandler;
mountedApp._updateHeaderHeightVar = updateHeaderHeightVar;
console.log('✅ MobileHeader component successfully mounted with LanguageProvider en dynamische headerhoogte');
return mountedApp;
} catch (error) {
console.error('🚨 [CRITICAL ERROR] Bij initialiseren mobile header:', error);
}
}
/**
* Initialiseert de chat app (Vue component)
*/
function initializeChatApp() {
const container = document.querySelector('.chat-container');
if (!container) {
console.error('🚨 [CRITICAL ERROR] .chat-container niet gevonden');
return;
}
try {
if (!ChatApp) {
throw new Error('🚨 [CRITICAL ERROR] ChatApp component niet gevonden');
}
// Extra verificatie dat alle sub-componenten beschikbaar zijn
if (!Components.MessageHistory || !Components.ChatInput ||
!Components.TypingIndicator || !Components.ChatMessage) {
console.warn('⚠️ [WARN] Niet alle benodigde sub-componenten zijn geladen!');
}
// Maak props voor de component
const props = {
apiPrefix: window.chatConfig.apiPrefix || '',
conversationId: window.chatConfig.conversationId || 'default',
userId: window.chatConfig.userId || null,
userName: window.chatConfig.userName || '',
initialLanguage: window.chatConfig.language || 'nl',
supportedLanguageDetails: window.chatConfig.supportedLanguageDetails || {},
allowedLanguages: window.chatConfig.allowedLanguages || ['nl', 'en', 'fr', 'de']
};
// Mount de component via ChatRoot zodat SafeViewport de layout kan beheren
const app = createApp(ChatRoot, props);
// SSE verbinding configuratie - injecteren in ChatApp component
app.provide('sseConfig', {
maxRetries: 3,
retryDelay: 2000,
handleSseError: function(error, taskId) {
console.warn(`SSE verbinding voor task ${taskId} mislukt:`, error);
// Fallback naar polling als SSE faalt
return this.fallbackToPolling(taskId);
},
fallbackToPolling: function(taskId) {
console.log(`Fallback naar polling voor task ${taskId}`);
// Polling implementatie - elke 3 seconden status checken
const pollingInterval = setInterval(() => {
const endpoint = `${props.apiPrefix}/api/task_status/${taskId}`;
fetch(endpoint)
.then(response => {
if (!response.ok) throw new Error(`Task status endpoint error: ${response.status}`);
return response.json();
})
.then(data => {
// Dispatch event om dezelfde event interface als SSE te behouden
const mockEvent = new CustomEvent('message', {
detail: { data: JSON.stringify(data) }
});
document.dispatchEvent(new CustomEvent(`sse:${taskId}:message`, { detail: mockEvent }));
// Stop polling als taak klaar is
if (data.status === 'completed' || data.status === 'failed' || data.status === 'cancelled') {
clearInterval(pollingInterval);
}
})
.catch(err => console.error('Polling error:', err));
}, 3000);
return pollingInterval;
}
});
// Registreer alle componenten globaal
Object.entries(Components).forEach(([name, component]) => {
app.component(name, component);
});
// Registreer error handler
app.config.errorHandler = (err, vm, info) => {
console.error('🚨 [Vue Error in ChatApp]', err);
console.error('Component:', vm);
console.error('Error Info:', info);
};
// Mount de component
const mountedApp = app.mount(container);
// Bewaar referentie globaal voor debugging
window.__chatApp = mountedApp;
// Bewaar een referentie naar de Vue instantie voor gebruik door andere componenten
window.__vueApp = app;
} catch (error) {
console.error('🚨 [CRITICAL ERROR] Bij initialiseren chat app:', error);
console.error('Stack trace:', error.stack);
// Fallback naar placeholder
container.innerHTML = '<div>Fout bij het initialiseren van de chat applicatie</div>';
}
}
/**
* Helper functie om Vue 3 element refs te verwerken
* Oplossing voor $el.querySelector problemen
*/
window.getElementFromRef = function(ref) {
if (!ref) return null;
// Vue 3 refs zijn reactieve objecten met een .value eigenschap
if (ref.value && ref.value instanceof HTMLElement) {
return ref.value;
}
// Directe HTML element refs
if (ref instanceof HTMLElement) {
return ref;
}
// Vue 3 component instance (geen $el meer in Vue 3)
if (ref.$el && ref.$el instanceof HTMLElement) {
return ref.$el;
}
// Voor template refs van Vue 3 mounted componenten
if (typeof ref === 'object' && ref !== null) {
const element = ref.el || ref.$el || ref.value;
if (element instanceof HTMLElement) {
return element;
}
}
return null;
}
console.log('Chat client modules geladen en gebundeld met moderne ES module structuur.');