diff --git a/common/models/user.py b/common/models/user.py index d69c440..5274cc1 100644 --- a/common/models/user.py +++ b/common/models/user.py @@ -346,9 +346,9 @@ class TranslationCache(db.Model): class PartnerRAGRetriever(db.Model): __bind_key__ = 'public' __table_args__ = ( - {'schema': 'public'}, db.PrimaryKeyConstraint('tenant_id', 'retriever_id'), - db.UniqueConstraint('partner_id', 'tenant_id', 'retriever_id') + db.UniqueConstraint('partner_id', 'tenant_id', 'retriever_id'), + {'schema': 'public'}, ) partner_id = db.Column(db.Integer, db.ForeignKey('public.partner.id'), nullable=False) diff --git a/docker/rebuild_chat_client.sh b/docker/rebuild_chat_client.sh index 3c05bac..4416a09 100755 --- a/docker/rebuild_chat_client.sh +++ b/docker/rebuild_chat_client.sh @@ -1,9 +1,16 @@ #!/bin/bash +cd /Volumes/OWC4M2_1/Development/Josako/EveAI/TBD/docker source ./docker_env_switch.sh dev -source .env dcdown eveai_chat_client nginx -./update_chat_client_statics.sh + +cd ../nginx + +npm run clean +npm run build + +cd ../docker ./build_and_push_eveai.sh -b nginx + dcup eveai_chat_client nginx diff --git a/eveai_chat_client/static/assets/js/chat-app.js b/eveai_chat_client/static/assets/js/ChatApp.js similarity index 69% rename from eveai_chat_client/static/assets/js/chat-app.js rename to eveai_chat_client/static/assets/js/ChatApp.js index 6d10b22..4d8d6bd 100644 --- a/eveai_chat_client/static/assets/js/chat-app.js +++ b/eveai_chat_client/static/assets/js/ChatApp.js @@ -1,24 +1,8 @@ -// Import all components -import { TypingIndicator } from './components/TypingIndicator.js'; -import { FormField } from './components/FormField.js'; -import { DynamicForm } from './components/DynamicForm.js'; -import { ChatMessage } from './components/ChatMessage.js'; -import { MessageHistory } from './components/MessageHistory.js'; -import { ProgressTracker } from './components/ProgressTracker.js'; -import { LanguageSelector } from './components/LanguageSelector.js'; - -// Maak componenten globaal beschikbaar voordat andere componenten worden geladen -window.DynamicForm = DynamicForm; -window.FormField = FormField; -window.TypingIndicator = TypingIndicator; -window.ChatMessage = ChatMessage; -window.MessageHistory = MessageHistory; -window.ProgressTracker = ProgressTracker; - -// Nu kunnen we ChatInput importeren nadat de benodigde componenten globaal beschikbaar zijn -import { ChatInput } from './components/ChatInput.js'; +// Import all components via barrel export +import { TypingIndicator, FormField, DynamicForm, ChatMessage, MessageHistory, ProgressTracker, LanguageSelector, ChatInput } from './components/index.js'; // Main Chat Application +// Main Chat Application - geΓ«xporteerd als module export const ChatApp = { name: 'ChatApp', components: { @@ -27,6 +11,7 @@ export const ChatApp = { DynamicForm, ChatMessage, MessageHistory, + ProgressTracker, ChatInput }, @@ -36,12 +21,18 @@ export const ChatApp = { const settings = chatConfig.settings || {}; const initialLanguage = chatConfig.language || 'nl'; const originalExplanation = chatConfig.explanation || ''; + const tenantMake = chatConfig.tenantMake || {}; return { + // Tenant info + tenantName: tenantMake.name || 'EveAI', + tenantLogoUrl: tenantMake.logo_url || '', + // Taal gerelateerde data - currentLanguage: '', + currentLanguage: initialLanguage, supportedLanguageDetails: chatConfig.supportedLanguageDetails || {}, allowedLanguages: chatConfig.allowedLanguages || ['nl', 'en', 'fr', 'de'], + supportedLanguages: chatConfig.supportedLanguages || [], originalExplanation: originalExplanation, explanation: chatConfig.explanation || '', @@ -88,7 +79,7 @@ export const ChatApp = { if (typeof marked === 'function') { return marked(this.explanation); } else if (marked && typeof marked.parse === 'function') { - return marked.parse(this.explanation); + return marked.parse(this.explanation.replace(/\[\[(.*?)\]\]/g, '$1')); } else { console.error('Marked library not properly loaded'); return this.explanation; @@ -101,18 +92,64 @@ export const ChatApp = { hasMessages() { return this.allMessages.length > 0; + }, + + displayLanguages() { + // Filter de ondersteunde talen op basis van de toegestane talen + if (!this.supportedLanguages || !this.allowedLanguages) { + return []; + } + + return this.supportedLanguages.filter(lang => + this.allowedLanguages.includes(lang.code) + ); } }, mounted() { + console.log('πŸ” [DEBUG] ChatApp mounted'); + console.log('πŸ” [DEBUG] ChatApp data:', { + tenantName: this.tenantName, + currentLanguage: this.currentLanguage, + allowedLanguages: this.allowedLanguages, + conversationId: this.conversationId, + userId: this.userId + }); + + // Log beschikbare componenten + console.log('πŸ” [DEBUG] Geregistreerde componenten:', { + messageHistory: !!this.$options.components.MessageHistory, + chatInput: !!this.$options.components.ChatInput + }); + + // Render de component + this.renderComponent(); + + console.log('πŸ” [DEBUG] ChatApp mounted'); + console.log('πŸ” [DEBUG] Props en data:', { + currentLanguage: this.currentLanguage, + allowedLanguages: this.allowedLanguages, + tenantName: this.tenantName + }); + this.initializeChat(); this.setupEventListeners(); + + // Render de component + this.renderComponent(); }, beforeUnmount() { this.cleanup(); }, + // Nieuwe methode om de component direct te renderen + renderComponent() { + console.log('πŸ” [DEBUG] ChatApp.renderComponent() aangeroepen'); + // Hier kunnen we directe DOM-manipulatie toevoegen indien nodig + // Vergelijkbaar met LanguageSelector.renderComponent() + }, + methods: { // Initialization initializeChat() { @@ -574,6 +611,65 @@ export const ChatApp = { } }, + submitForm(formValues) { + console.log('πŸ” [DEBUG] Formulier verzenden:', formValues); + if (!formValues || this.isLoading) return; + + // Markeer formulier als bezig met verzenden + this.isSubmittingForm = true; + + // Voeg gebruikersformulier toe aan chat + this.addFormMessage(formValues, 'user'); + + // Verstuur naar backend + this.sendFormToBackend(formValues); + + // Reset input form + this.currentInputFormData = null; + }, + + sendFormToBackend(formValues) { + console.log('πŸ” [DEBUG] Formulier naar backend versturen', formValues); + // Implementatie voor het versturen van formulier data + this.isSubmittingForm = false; + }, + + sendMessageToBackend(message) { + console.log('πŸ” [DEBUG] Bericht naar backend versturen', message); + // Implementatie voor het versturen van berichten + }, + + addFormMessage(formValues, sender) { + console.log('πŸ” [DEBUG] Formulier bericht toevoegen:', formValues); + // Implementatie voor het toevoegen van een formulier bericht + }, + + focusChatInput() { + // Focus op de chat input als deze beschikbaar is + this.$nextTick(() => { + // Controleer of $el beschikbaar is + if (!this.$el) { + console.warn('$el is niet beschikbaar in focusChatInput'); + return; + } + + // Probeer de input te vinden en focus + try { + const chatInputComponent = this.$el.querySelector('.chat-input-container'); + if (chatInputComponent) { + console.log('πŸ” [DEBUG] Focus op chat input'); + // Probeer een input element te vinden om focus op te zetten + const inputElement = chatInputComponent.querySelector('input, textarea'); + if (inputElement) { + inputElement.focus(); + } + } + } catch (error) { + console.error('Fout bij focussen op chat input:', error); + } + }); + }, + removeMessage(messageId) { const index = this.allMessages.findIndex(m => m.id === messageId); if (index !== -1) { @@ -791,14 +887,186 @@ export const ChatApp = { return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; }, - focusChatInput() { - this.$refs.chatInput?.focusInput(); - }, - focusSearch() { this.$refs.searchInput?.focus(); }, + renderComponent() { + console.log('πŸ” [DEBUG] ChatApp.renderComponent() aangeroepen'); + console.log('πŸ” [DEBUG] window.Vue beschikbaar:', !!window.Vue); + if (window.Vue) { + console.log('πŸ” [DEBUG] window.Vue.createApp beschikbaar:', !!window.Vue.createApp); + console.log('πŸ” [DEBUG] window.Vue.version:', window.Vue.version); + } + + // We gaan direct de container manipuleren + const container = document.querySelector('.chat-container'); + if (!container) { + console.error('Container niet gevonden voor ChatApp (.chat-container)'); + return; + } + + console.log('πŸ” [DEBUG] ChatApp container gevonden, inhoud wordt gerenderd'); + console.log('πŸ” [DEBUG] Beschikbare components:', { + MessageHistory: !!window.Components?.MessageHistory, + ChatInput: !!window.Components?.ChatInput + }); + + // Basis layout voor de chat app + container.innerHTML = ` +
+ +
+ + +
+
+ `; + + // Nu we de DOM structuur hebben, gaan we de componenten initialiseren + const messageHistoryContainer = document.getElementById('chat-message-history'); + const chatInputContainer = document.getElementById('chat-input-container'); + + // Instantieer MessageHistory component met Vue + if (messageHistoryContainer) { + console.log('πŸ” [DEBUG] MessageHistory container gevonden, initialiseren...'); + try { + // Maak props voor de MessageHistory component + const messageHistoryProps = { + messages: this.allMessages, + isTyping: this.isTyping, + isSubmittingForm: this.isSubmittingForm, + apiPrefix: this.apiPrefix, + autoScroll: true + }; + + // Maak een nieuwe Vue app en registreer alle componenten + const messageHistoryApp = window.Vue.createApp(MessageHistory, messageHistoryProps); + + // Registreer benodigde componenten voor MessageHistory + messageHistoryApp.component('chat-message', ChatMessage); + messageHistoryApp.component('typing-indicator', TypingIndicator); + messageHistoryApp.component('progress-tracker', ProgressTracker); + messageHistoryApp.component('dynamic-form', DynamicForm); + + // Error handler + messageHistoryApp.config.errorHandler = (err, vm, info) => { + console.error('🚨 [Vue Error in MessageHistory]', err); + console.error('Component:', vm); + console.error('Error Info:', info); + }; + + // Geef events door naar de ChatApp + messageHistoryApp.config.globalProperties.$emit = (event, ...args) => { + if (event === 'specialist-complete') { + this.handleSpecialistComplete(args[0]); + } else if (event === 'specialist-error') { + this.handleSpecialistError(args[0]); + } + }; + + // Mount de component en bewaar de referentie - controleer of renderComponent bestaat + let messageHistoryInstance = null; + if (typeof MessageHistory.renderComponent === 'function') { + messageHistoryInstance = MessageHistory.renderComponent( + messageHistoryContainer, + messageHistoryProps, + messageHistoryApp + ); + } else { + // Fallback: direct mounten + messageHistoryInstance = messageHistoryApp.mount(messageHistoryContainer); + console.log('πŸ” [DEBUG] MessageHistory direct gemount via app.mount()'); + } + + // Bewaar de instantie voor toekomstige updates + this.$refs.messageHistory = messageHistoryInstance; + console.log('πŸ” [DEBUG] MessageHistory component succesvol geΓ―nitialiseerd'); + } catch (error) { + console.error('🚨 [ERROR] Fout bij initialiseren MessageHistory:', error); + } + } else { + console.error('MessageHistory container niet gevonden in de DOM'); + } + + // Instantieer ChatInput component met Vue + if (chatInputContainer) { + console.log('πŸ” [DEBUG] ChatInput container gevonden, initialiseren...'); + try { + // Maak props voor de ChatInput component + const chatInputProps = { + currentMessage: this.currentMessage, + isLoading: this.isLoading, + maxLength: this.settings.maxMessageLength, + formData: this.currentInputFormData, + allowFileUpload: this.settings.allowFileUpload, + allowVoiceMessage: this.settings.allowVoiceMessage + }; + + // Maak een nieuwe Vue app en registreer alle componenten + const chatInputApp = window.Vue.createApp(ChatInput, chatInputProps); + + // Registreer benodigde componenten voor ChatInput + chatInputApp.component('dynamic-form', DynamicForm); + chatInputApp.component('form-field', FormField); + + // Error handler + chatInputApp.config.errorHandler = (err, vm, info) => { + console.error('🚨 [Vue Error in ChatInput]', err); + console.error('Component:', vm); + console.error('Error Info:', info); + }; + + // Geef events door naar de ChatApp + chatInputApp.config.globalProperties.$emit = (event, ...args) => { + if (event === 'send-message') { + this.sendMessage(); + } else if (event === 'update-message') { + this.updateCurrentMessage(args[0]); + } else if (event === 'submit-form') { + this.submitFormFromInput(args[0]); + } else if (event === 'upload-file') { + this.handleFileUpload(args[0]); + } else if (event === 'record-voice') { + this.handleVoiceRecord(args[0]); + } + }; + + // Mount de component en bewaar de referentie - controleer of renderComponent bestaat + let chatInputInstance = null; + if (typeof ChatInput.renderComponent === 'function') { + console.log('πŸ” [DEBUG] ChatInput.renderComponent wordt aangeroepen met chatInputApp:', chatInputApp); + console.log('πŸ” [DEBUG] chatInputApp.mount type:', typeof chatInputApp.mount); + chatInputInstance = ChatInput.renderComponent( + chatInputContainer, + chatInputProps, + chatInputApp + ); + } else { + // Fallback: direct mounten + console.log('πŸ” [DEBUG] ChatInput wordt direct gemount via app.mount()'); + chatInputInstance = chatInputApp.mount(chatInputContainer); + console.log('πŸ” [DEBUG] ChatInput direct gemount via app.mount()'); + } + + // Bewaar de instantie voor toekomstige updates + this.$refs.chatInput = chatInputInstance; + console.log('πŸ” [DEBUG] ChatInput component succesvol geΓ―nitialiseerd'); + } catch (error) { + console.error('🚨 [ERROR] Fout bij initialiseren ChatInput:', error); + } + } else { + console.error('ChatInput container niet gevonden in de DOM'); + } + + console.log('πŸ” [DEBUG] ChatApp succesvol gerenderd met alle componenten'); + }, + + }, + + // Voeg een minimale render functie toe (vergelijkbaar met LanguageSelector) + render() { + return document.createElement('div'); }, template: ` @@ -835,101 +1103,4 @@ export const ChatApp = { ` - }; - -// Zorg ervoor dat alle componenten correct geΓ―nitialiseerd zijn voordat ze worden gebruikt -const initializeApp = () => { - console.log('Initializing Chat Application'); - - // ChatInput wordt pas op dit punt globaal beschikbaar gemaakt - // omdat het afhankelijk is van andere componenten - window.ChatInput = ChatInput; - - // Get access to the existing Vue app instance - if (window.__vueApp) { - - // Register ALL components globally - window.__vueApp.component('TypingIndicator', TypingIndicator); - window.__vueApp.component('FormField', FormField); - window.__vueApp.component('DynamicForm', DynamicForm); - window.__vueApp.component('ChatMessage', ChatMessage); - window.__vueApp.component('MessageHistory', MessageHistory); - window.__vueApp.component('ChatInput', ChatInput); - window.__vueApp.component('ProgressTracker', ProgressTracker); - // NB: LanguageSelector wordt niet globaal geregistreerd omdat deze apart gemonteerd wordt - console.log('All chat components registered with existing Vue instance'); - - // Register the ChatApp component - window.__vueApp.component('ChatApp', ChatApp); - console.log('ChatApp component registered with existing Vue instance'); - - // Mount the Vue app - window.__vueApp.mount('#app'); - console.log('Vue app mounted with chat components'); - - } else { - console.error('No existing Vue instance found on window.__vueApp'); - } -}; - -// Functie om LanguageSelector toe te voegen aan sidebar -const mountLanguageSelector = () => { - const container = document.getElementById('language-selector-container'); - if (container) { - // Maak een eenvoudige Vue app die alleen de LanguageSelector component mount - const app = Vue.createApp({ - components: { LanguageSelector }, - data() { - return { - currentLanguage: window.chatConfig?.language || 'nl', - supportedLanguageDetails: window.chatConfig?.supportedLanguageDetails || {}, - allowedLanguages: window.chatConfig?.allowedLanguages || ['nl', 'en', 'fr', 'de'] - }; - }, - methods: { - handleLanguageChange(newLanguage) { - console.log(`LanguageSelector: Taal gewijzigd naar ${newLanguage}`); - - // Gebruik ALLEEN de custom event benadering - const event = new CustomEvent('language-changed', { - detail: { language: newLanguage } - }); - document.dispatchEvent(event); - } - }, - template: ` - - ` - }); - - app.component('LanguageSelector', LanguageSelector); - app.mount('#language-selector-container'); - console.log('Language selector mounted in sidebar'); - } else { - console.warn('Language selector container not found'); - } -}; - -// Initialize app when DOM is ready -document.addEventListener('DOMContentLoaded', () => { - console.log('DOM content loaded, initializing application...'); - - // Eerst de hoofdapplicatie initialiseren - initializeApp(); - - // Dan de taal selector monteren met een kleine vertraging - // om er zeker van te zijn dat de container is aangemaakt - setTimeout(() => { - try { - mountLanguageSelector(); - } catch (e) { - console.error('Fout bij het monteren van de taal selector:', e); - } - }, 200); -}); \ No newline at end of file diff --git a/eveai_chat_client/static/assets/js/components/ChatInput.js b/eveai_chat_client/static/assets/js/components/ChatInput.js index 96936e5..768ff80 100644 --- a/eveai_chat_client/static/assets/js/components/ChatInput.js +++ b/eveai_chat_client/static/assets/js/components/ChatInput.js @@ -1,35 +1,89 @@ // static/js/components/ChatInput.js - // Importeer de IconManager (als module systeem wordt gebruikt) - // Anders moet je ervoor zorgen dat MaterialIconManager.js eerder wordt geladen - // en iconManager beschikbaar is via window.iconManager + // Importeer de benodigde componenten + import { DynamicForm } from './DynamicForm.js'; + import { IconManagerMixin } from '../iconManager.js'; -// Voeg stylesheet toe voor ChatInput-specifieke stijlen -const addStylesheet = () => { - if (!document.querySelector('link[href*="chat-input.css"]')) { - const link = document.createElement('link'); - link.rel = 'stylesheet'; - link.href = '/static/assets/css/chat-input.css'; - document.head.appendChild(link); - } -}; - -// Laad de stylesheet -addStylesheet(); +// CSS wordt nu geladen via de main bundle in chat-client.js +// We hoeven hier niets dynamisch te laden export const ChatInput = { name: 'ChatInput', components: { - 'dynamic-form': window.DynamicForm + 'dynamic-form': DynamicForm }, - created() { - // Als module systeem wordt gebruikt: - // import { iconManager } from './MaterialIconManager.js'; - // Anders gebruiken we window.iconManager als het beschikbaar is: - if (window.iconManager && this.formData && this.formData.icon) { - window.iconManager.ensureIconsLoaded({}, [this.formData.icon]); + // Static method for direct rendering + renderComponent(container, props, app) { + console.log('πŸ” [DEBUG] ChatInput.renderComponent() aangeroepen'); + console.log('πŸ” [DEBUG] ChatInput container:', container); + console.log('πŸ” [DEBUG] ChatInput props:', props); + console.log('πŸ” [DEBUG] ChatInput app:', app); + + if (!container) { + console.error('Container element niet gevonden voor ChatInput'); + return null; } + // Controleer de globale dependencies + console.log('πŸ” [DEBUG] Global dependencies check:'); + console.log('- window.Vue:', typeof window.Vue); + if (window.Vue) { + console.log('- window.Vue.createApp:', typeof window.Vue.createApp); + console.log('- window.Vue.version:', window.Vue.version); + } + + console.log('πŸ” [DEBUG] ChatInput container gevonden, Vue app aan het initialiseren'); + + try { + // We controleren het app object + if (!app) { + console.error('🚨 [ERROR] Geen Vue app object ontvangen'); + return null; + } + + // Check of we een correcte Vue app hebben of we moeten er een maken + if (typeof app.mount !== 'function') { + console.log('πŸ” [DEBUG] Ontvangen app heeft geen mount functie, dit is mogelijk een config object'); + // Controleer of window.Vue beschikbaar is + if (!window.Vue || typeof window.Vue.createApp !== 'function') { + console.error('🚨 [ERROR] window.Vue.createApp is niet beschikbaar'); + return null; + } + + // Maak een nieuwe Vue app met het ChatInput component en de props + console.log('πŸ” [DEBUG] Nieuwe Vue app aanmaken met ChatInput component'); + try { + app = window.Vue.createApp(ChatInput, props); + console.log('πŸ” [DEBUG] Nieuwe app aangemaakt:', app); + } catch (createError) { + console.error('🚨 [ERROR] Fout bij aanmaken Vue app:', createError); + // Probeer een alternatieve aanpak zonder importreferenties + console.log('πŸ” [DEBUG] Alternatieve aanpak proberen...'); + const componentCopy = JSON.parse(JSON.stringify(ChatInput)); + app = window.Vue.createApp(componentCopy, props); + } + } + + // Stel een fallback DOM in voor het geval mounten mislukt + container.innerHTML = `
Chat input laden...
`; + + // Nu kunnen we de app mounten + console.log('πŸ” [DEBUG] App.mount aanroepen op container'); + const instance = app.mount(container); + console.log('πŸ” [DEBUG] ChatInput component succesvol gemount, instance:', instance); + return instance; + } catch (error) { + console.error('🚨 [ERROR] Fout bij mounten ChatInput component:', error); + console.error('Error stack:', error.stack); + return null; + } + }, + // Gebruik de IconManagerMixin om automatisch iconen te laden + mixins: [IconManagerMixin], + created() { + // Als er een formData.icon is, wordt deze automatisch geladen via IconManagerMixin + // Geen expliciete window.iconManager calls meer nodig + // Maak een benoemde handler voor betere cleanup this.languageChangeHandler = (event) => { if (event.detail && event.detail.language) { @@ -342,10 +396,7 @@ export const ChatInput = { }, template: `
- -
- -
+
diff --git a/eveai_chat_client/static/assets/js/components/ChatMessage.js b/eveai_chat_client/static/assets/js/components/ChatMessage.js index f3a6a0c..2df1391 100644 --- a/eveai_chat_client/static/assets/js/components/ChatMessage.js +++ b/eveai_chat_client/static/assets/js/components/ChatMessage.js @@ -1,35 +1,62 @@ -// Voeg stylesheets toe voor formulier en chat berichten weergave -const addStylesheets = () => { - // Formulier stylesheet - if (!document.querySelector('link[href*="form-message.css"]')) { - const formLink = document.createElement('link'); - formLink.rel = 'stylesheet'; - formLink.href = '/static/assets/css/form-message.css'; - document.head.appendChild(formLink); - } +// Import benodigde componenten +import { DynamicForm } from './DynamicForm.js'; +import { ProgressTracker } from './ProgressTracker.js'; - // Chat bericht stylesheet - if (!document.querySelector('link[href*="chat-message.css"]')) { - const chatLink = document.createElement('link'); - chatLink.rel = 'stylesheet'; - chatLink.href = '/static/assets/css/chat-message.css'; - document.head.appendChild(chatLink); - } - - // Material Icons font stylesheet - if (!document.querySelector('link[href*="Material+Symbols+Outlined"]')) { - const iconLink = document.createElement('link'); - iconLink.rel = 'stylesheet'; - iconLink.href = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0'; - document.head.appendChild(iconLink); - } -}; - -// Laad de stylesheets -addStylesheets(); +// CSS en Material Icons worden nu geladen via de hoofdbundel en scripts.html +// We hoeven hier niets dynamisch te laden export const ChatMessage = { name: 'ChatMessage', + // Static method for direct rendering + renderComponent(container, props, app) { + console.log('πŸ” [DEBUG] ChatMessage.renderComponent() aangeroepen'); + console.log('πŸ” [DEBUG] ChatMessage container:', container); + console.log('πŸ” [DEBUG] ChatMessage props:', props); + + if (!container) { + console.error('Container element niet gevonden voor ChatMessage'); + return null; + } + + console.log('πŸ” [DEBUG] ChatMessage container gevonden, Vue app aan het initialiseren'); + + try { + // We controleren het app object + if (!app) { + console.error('🚨 [ERROR] Geen Vue app object ontvangen'); + return null; + } + + // Check of we een correcte Vue app hebben of we moeten er een maken + if (typeof app.mount !== 'function') { + console.log('πŸ” [DEBUG] Ontvangen app heeft geen mount functie, dit is mogelijk een config object'); + // Controleer of window.Vue beschikbaar is + if (!window.Vue || typeof window.Vue.createApp !== 'function') { + console.error('🚨 [ERROR] window.Vue.createApp is niet beschikbaar'); + return null; + } + + // Maak een nieuwe Vue app met het ChatMessage component en de props + console.log('πŸ” [DEBUG] Nieuwe Vue app aanmaken met ChatMessage component'); + app = window.Vue.createApp(ChatMessage, props); + console.log('πŸ” [DEBUG] Nieuwe app aangemaakt:', app); + } + + // Nu kunnen we de app mounten + console.log('πŸ” [DEBUG] App.mount aanroepen op container'); + const instance = app.mount(container); + console.log('πŸ” [DEBUG] ChatMessage component succesvol gemount'); + return instance; + } catch (error) { + console.error('🚨 [ERROR] Fout bij mounten ChatMessage component:', error); + console.error('Error stack:', error.stack); + return null; + } + }, + components: { + 'dynamic-form': DynamicForm, + 'progress-tracker': ProgressTracker + }, props: { message: { type: Object, diff --git a/eveai_chat_client/static/assets/js/components/DynamicForm.js b/eveai_chat_client/static/assets/js/components/DynamicForm.js index c44e973..021c8e3 100644 --- a/eveai_chat_client/static/assets/js/components/DynamicForm.js +++ b/eveai_chat_client/static/assets/js/components/DynamicForm.js @@ -1,5 +1,26 @@ export const DynamicForm = { name: 'DynamicForm', + // Static method for direct rendering + renderComponent(container, props, app) { + console.log('πŸ” [DEBUG] DynamicForm.renderComponent() aangeroepen'); + + if (!container) { + console.error('Container element niet gevonden voor DynamicForm'); + return null; + } + + console.log('πŸ” [DEBUG] DynamicForm container gevonden, Vue app aanmaken'); + + try { + // Maak een nieuwe Vue app instantie met dit component + const instance = app.mount(container); + console.log('πŸ” [DEBUG] DynamicForm component succesvol gemount'); + return instance; + } catch (error) { + console.error('🚨 [ERROR] Fout bij mounten DynamicForm component:', error); + return null; + } + }, created() { // Zorg ervoor dat het icoon geladen wordt als iconManager beschikbaar is if (window.iconManager && this.formData && this.formData.icon) { diff --git a/eveai_chat_client/static/assets/js/components/FormField.js b/eveai_chat_client/static/assets/js/components/FormField.js index 7a25b11..362ac7e 100644 --- a/eveai_chat_client/static/assets/js/components/FormField.js +++ b/eveai_chat_client/static/assets/js/components/FormField.js @@ -1,5 +1,26 @@ export const FormField = { name: 'FormField', + // Static method for direct rendering + renderComponent(container, props, app) { + console.log('πŸ” [DEBUG] FormField.renderComponent() aangeroepen'); + + if (!container) { + console.error('Container element niet gevonden voor FormField'); + return null; + } + + console.log('πŸ” [DEBUG] FormField container gevonden, Vue app aanmaken'); + + try { + // Maak een nieuwe Vue app instantie met dit component + const instance = app.mount(container); + console.log('πŸ” [DEBUG] FormField component succesvol gemount'); + return instance; + } catch (error) { + console.error('🚨 [ERROR] Fout bij mounten FormField component:', error); + return null; + } + }, props: { field: { type: Object, diff --git a/eveai_chat_client/static/assets/js/components/FormMessage.js b/eveai_chat_client/static/assets/js/components/FormMessage.js index b381d3f..4558915 100644 --- a/eveai_chat_client/static/assets/js/components/FormMessage.js +++ b/eveai_chat_client/static/assets/js/components/FormMessage.js @@ -2,6 +2,52 @@ export const FormMessage = { name: 'FormMessage', + // Static method for direct rendering + renderComponent(container, props, app) { + console.log('πŸ” [DEBUG] FormMessage.renderComponent() aangeroepen'); + console.log('πŸ” [DEBUG] FormMessage container:', container); + console.log('πŸ” [DEBUG] FormMessage props:', props); + + if (!container) { + console.error('Container element niet gevonden voor FormMessage'); + return null; + } + + console.log('πŸ” [DEBUG] FormMessage container gevonden, Vue app aan het initialiseren'); + + try { + // We controleren het app object + if (!app) { + console.error('🚨 [ERROR] Geen Vue app object ontvangen'); + return null; + } + + // Check of we een correcte Vue app hebben of we moeten er een maken + if (typeof app.mount !== 'function') { + console.log('πŸ” [DEBUG] Ontvangen app heeft geen mount functie, dit is mogelijk een config object'); + // Controleer of window.Vue beschikbaar is + if (!window.Vue || typeof window.Vue.createApp !== 'function') { + console.error('🚨 [ERROR] window.Vue.createApp is niet beschikbaar'); + return null; + } + + // Maak een nieuwe Vue app met het FormMessage component en de props + console.log('πŸ” [DEBUG] Nieuwe Vue app aanmaken met FormMessage component'); + app = window.Vue.createApp(FormMessage, props); + console.log('πŸ” [DEBUG] Nieuwe app aangemaakt:', app); + } + + // Nu kunnen we de app mounten + console.log('πŸ” [DEBUG] App.mount aanroepen op container'); + const instance = app.mount(container); + console.log('πŸ” [DEBUG] FormMessage component succesvol gemount'); + return instance; + } catch (error) { + console.error('🚨 [ERROR] Fout bij mounten FormMessage component:', error); + console.error('Error stack:', error.stack); + return null; + } + }, props: { formData: { type: Object, diff --git a/eveai_chat_client/static/assets/js/components/LanguageSelector.js b/eveai_chat_client/static/assets/js/components/LanguageSelector.js index f386d19..616a76b 100644 --- a/eveai_chat_client/static/assets/js/components/LanguageSelector.js +++ b/eveai_chat_client/static/assets/js/components/LanguageSelector.js @@ -1,4 +1,3 @@ - export const LanguageSelector = { name: 'LanguageSelector', props: { @@ -6,6 +5,10 @@ export const LanguageSelector = { type: String, default: 'nl' }, + currentLanguage: { + type: String, + default: null + }, supportedLanguageDetails: { type: Object, default: () => ({}) @@ -13,22 +16,44 @@ export const LanguageSelector = { allowedLanguages: { type: Array, default: () => ['nl', 'en', 'fr', 'de'] - } + }, }, - computed: { - availableLanguages() { - // Maak een array van taalobjecten op basis van de toegestane talen - // en de ondersteunde taaldetails - const languages = []; + data() { + const startLanguage = this.currentLanguage || this.initialLanguage; + return { + selectedLanguage: startLanguage, + internalCurrentLanguage: startLanguage + }; + }, + mounted() { + // console.log('πŸ” [DEBUG] LanguageSelector mounted'); + // console.log('πŸ” [DEBUG] Props:', { + // initialLanguage: this.initialLanguage, + // currentLanguage: this.currentLanguage, + // supportedLanguageDetails: this.supportedLanguageDetails, + // allowedLanguages: this.allowedLanguages + // }); - // Als er geen toegestane talen zijn of de lijst is leeg, gebruik een standaardlijst - const languagesToUse = (this.allowedLanguages && this.allowedLanguages.length > 0) - ? this.allowedLanguages + // Render the component + this.renderComponent(); + + // Emit initial language + this.$emit('language-changed', this.selectedLanguage); + + // DOM event + const event = new CustomEvent('vue:language-changed', { + detail: { language: this.selectedLanguage } + }); + document.dispatchEvent(event); + }, + methods: { + getAvailableLanguages() { + const languages = []; + const languagesToUse = (this.allowedLanguages && this.allowedLanguages.length > 0) + ? this.allowedLanguages : ['nl', 'en', 'fr', 'de']; - // Loop door alle ondersteunde taaldetails if (this.supportedLanguageDetails && Object.keys(this.supportedLanguageDetails).length > 0) { - // Vind talen die overeenkomen met toegestane taalcodes for (const [langName, langDetails] of Object.entries(this.supportedLanguageDetails)) { const isoCode = langDetails['iso 639-1']; if (languagesToUse.includes(isoCode)) { @@ -40,7 +65,6 @@ export const LanguageSelector = { } } } else { - // Fallback als er geen taaldetails beschikbaar zijn const defaultLanguages = { 'nl': { name: 'Nederlands', flag: 'πŸ‡³πŸ‡±' }, 'en': { name: 'English', flag: 'πŸ‡¬πŸ‡§' }, @@ -52,67 +76,87 @@ export const LanguageSelector = { if (defaultLanguages[code]) { languages.push({ code: code, - name: defaultLanguages[code].code, + name: defaultLanguages[code].name, flag: defaultLanguages[code].flag }); } }); } - console.log('LanguageSelector: availableLanguages computed:', languages); return languages; - } - }, - emits: ['language-changed'], - data() { - return { - selectedLanguage: this.initialLanguage, - currentLanguage: this.initialLanguage - }; - }, - mounted() { - console.log('LanguageSelector mounted with:', { - initialLanguage: this.initialLanguage, - selectedLanguage: this.selectedLanguage, - availableLanguages: this.availableLanguages - }); + }, - // Stuur het language-changed event uit met de initiΓ«le taal - console.log(`LanguageSelector: Emitting language-changed event for initial language ${this.initialLanguage}`); - this.$emit('language-changed', this.initialLanguage); - }, - methods: { changeLanguage(languageCode) { - console.log(`LanguageSelector: changeLanguage called with ${languageCode}, current: ${this.currentLanguage}`); + console.log(`LanguageSelector: changeLanguage called with ${languageCode}`); - if (this.currentLanguage !== languageCode) { - console.log(`LanguageSelector: Emitting language-changed event for ${languageCode}`); - this.currentLanguage = languageCode; + if (this.internalCurrentLanguage !== languageCode) { + this.internalCurrentLanguage = languageCode; + this.selectedLanguage = languageCode; + + // Vue component emit this.$emit('language-changed', languageCode); - } else { - console.log(`LanguageSelector: No change needed, already ${languageCode}`); + + // DOM event + const event = new CustomEvent('vue:language-changed', { + detail: { language: languageCode } + }); + document.dispatchEvent(event); + } + }, + + renderComponent() { + // We gaan direct de container manipuleren + const container = document.getElementById('language-selector-container'); + if (!container) { + console.error('Container niet gevonden voor LanguageSelector'); + return; + } + + const availableLanguages = this.getAvailableLanguages(); + // console.log('πŸ” [DEBUG] Available languages:', availableLanguages); + + const optionsHtml = availableLanguages.map(lang => + `` + ).join(''); + + container.innerHTML = ` +
+ +
+ +
+
+ `; + + // Add event listener + const selectElement = container.querySelector('#language-select'); + if (selectElement) { + selectElement.addEventListener('change', (e) => { + this.changeLanguage(e.target.value); + }); + } + + // console.log('πŸ” [DEBUG] Component rendered successfully'); } - } }, + + // Stap 1: Ondersteun zowel template-based rendering als manual rendering + // Door beide methoden te ondersteunen, werkt het component in beide scenario's template: `
- +
- ` + `, + + // Minimale render functie als fallback + render() { + return document.createElement('div'); + } }; \ No newline at end of file diff --git a/eveai_chat_client/static/assets/js/components/MessageHistory.js b/eveai_chat_client/static/assets/js/components/MessageHistory.js index 92add92..c041fef 100644 --- a/eveai_chat_client/static/assets/js/components/MessageHistory.js +++ b/eveai_chat_client/static/assets/js/components/MessageHistory.js @@ -1,5 +1,15 @@ -export const MessageHistory = { + // Import afhankelijke componenten + import { ChatMessage } from './ChatMessage.js'; + import { TypingIndicator } from './TypingIndicator.js'; + import { ProgressTracker } from './ProgressTracker.js'; + + export const MessageHistory = { name: 'MessageHistory', + components: { + 'chat-message': ChatMessage, + 'typing-indicator': TypingIndicator, + 'progress-tracker': ProgressTracker + }, props: { messages: { type: Array, @@ -13,10 +23,10 @@ export const MessageHistory = { isSubmittingForm: { type: Boolean, default: false - }, - apiPrefix: { - type: String, - default: '' + }, + apiPrefix: { + type: String, + default: '' }, autoScroll: { type: Boolean, @@ -216,5 +226,27 @@ export const MessageHistory = {
- `, + ` +}; + +// Statische renderComponent methode voor het MessageHistory object +MessageHistory.renderComponent = function(container, props, app) { + console.log('πŸ” [DEBUG] MessageHistory.renderComponent() aangeroepen als statische methode'); + + if (!container) { + console.error('Container element niet gevonden voor MessageHistory'); + return null; + } + + console.log('πŸ” [DEBUG] MessageHistory container gevonden, Vue app aanmaken'); + + try { + // Maak een nieuwe Vue app instantie met dit component + const instance = app.mount(container); + console.log('πŸ” [DEBUG] MessageHistory component succesvol gemount'); + return instance; + } catch (error) { + console.error('🚨 [ERROR] Fout bij mounten MessageHistory component:', error); + return null; + } }; \ No newline at end of file diff --git a/eveai_chat_client/static/assets/js/components/ProgressTracker.js b/eveai_chat_client/static/assets/js/components/ProgressTracker.js index 4b2311e..e08dbf0 100644 --- a/eveai_chat_client/static/assets/js/components/ProgressTracker.js +++ b/eveai_chat_client/static/assets/js/components/ProgressTracker.js @@ -308,4 +308,26 @@ export const ProgressTracker = { ` +}; + +// Voeg de renderComponent toe als statische methode op het ProgressTracker object +ProgressTracker.renderComponent = function(container, props, app) { + console.log('πŸ” [DEBUG] ProgressTracker.renderComponent() aangeroepen'); + + if (!container) { + console.error('Container element niet gevonden voor ProgressTracker'); + return null; + } + + console.log('πŸ” [DEBUG] ProgressTracker container gevonden, Vue app aanmaken'); + + try { + // Maak een nieuwe Vue app instantie met dit component + const instance = app.mount(container); + console.log('πŸ” [DEBUG] ProgressTracker component succesvol gemount'); + return instance; + } catch (error) { + console.error('🚨 [ERROR] Fout bij mounten ProgressTracker component:', error); + return null; + } }; \ No newline at end of file diff --git a/eveai_chat_client/static/assets/js/components/TypingIndicator.js b/eveai_chat_client/static/assets/js/components/TypingIndicator.js index be20bc7..4ac79c8 100644 --- a/eveai_chat_client/static/assets/js/components/TypingIndicator.js +++ b/eveai_chat_client/static/assets/js/components/TypingIndicator.js @@ -1,10 +1,46 @@ export const TypingIndicator = { name: 'TypingIndicator', + props: { + showText: { + type: Boolean, + default: false + }, + text: { + type: String, + default: 'Bezig met typen...' + } + }, + methods: { + // Andere methoden kunnen hier staan + }, template: `
+
{{ text }}
` +}; + +// Voeg statische renderComponent methode toe aan het TypingIndicator object +TypingIndicator.renderComponent = function(container, props, app) { + console.log('πŸ” [DEBUG] TypingIndicator.renderComponent() aangeroepen als statische methode'); + + if (!container) { + console.error('Container element niet gevonden voor TypingIndicator'); + return null; + } + + console.log('πŸ” [DEBUG] TypingIndicator container gevonden, Vue app aanmaken'); + + try { + // Maak een nieuwe Vue app instantie met dit component + const instance = app.mount(container); + console.log('πŸ” [DEBUG] TypingIndicator component succesvol gemount'); + return instance; + } catch (error) { + console.error('🚨 [ERROR] Fout bij mounten TypingIndicator component:', error); + return null; + } }; \ No newline at end of file diff --git a/eveai_chat_client/static/assets/js/components/index.js b/eveai_chat_client/static/assets/js/components/index.js new file mode 100644 index 0000000..39f6f1f --- /dev/null +++ b/eveai_chat_client/static/assets/js/components/index.js @@ -0,0 +1,34 @@ +// Component barrel export file +// Dit bestand maakt het eenvoudiger om alle componenten in één keer te importeren + +// Importeer eerst alle componenten lokaal +import { TypingIndicator } from './TypingIndicator.js'; +import { FormField } from './FormField.js'; +import { DynamicForm } from './DynamicForm.js'; +import { ChatMessage } from './ChatMessage.js'; +import { MessageHistory } from './MessageHistory.js'; +import { ProgressTracker } from './ProgressTracker.js'; +import { LanguageSelector } from './LanguageSelector.js'; +import { ChatInput } from './ChatInput.js'; + +// Exporteer componenten individueel +export { TypingIndicator }; +export { FormField }; +export { DynamicForm }; +export { ChatMessage }; +export { MessageHistory }; +export { ProgressTracker }; +export { LanguageSelector }; +export { ChatInput }; + +// Debug logging voor index.js +console.log('πŸ” [DEBUG] Components index.js geladen, exporteert:', { + TypingIndicator: typeof TypingIndicator, + ChatMessage: typeof ChatMessage, + MessageHistory: typeof MessageHistory, + ChatInput: typeof ChatInput +}); + +// Nu kunnen componenten op verschillende manieren worden geΓ―mporteerd: +// 1. import { FormField, ChatMessage } from './components'; +// 2. import { ChatInput } from './components/ChatInput.js'; diff --git a/eveai_chat_client/static/assets/js/iconManager.js b/eveai_chat_client/static/assets/js/iconManager.js index 3414455..9dbedc6 100644 --- a/eveai_chat_client/static/assets/js/iconManager.js +++ b/eveai_chat_client/static/assets/js/iconManager.js @@ -88,48 +88,43 @@ window.iconManager = { } }; -// Functie om iconManager toe te voegen aan het DynamicForm component -function initDynamicFormWithIcons() { - if (window.DynamicForm) { - const originalCreated = window.DynamicForm.created || function() {}; +// Export de iconManager functie om te gebruiken in Vue componenten +// Dit vervangt de complexe injectie in het DynamicForm component +export { iconManager as default }; - window.DynamicForm.created = function() { - // Roep de oorspronkelijke created methode aan als die bestond - originalCreated.call(this); +// We exporteren iconManager als default, maar houden ook de window.iconManager beschikbaar +// voor backwards compatibility met bestaande code - // Laad het icoon als het beschikbaar is - if (this.formData && this.formData.icon) { - window.iconManager.loadIcon(this.formData.icon); - } - }; - - // Voeg watcher toe voor formData.icon - if (!window.DynamicForm.watch) { - window.DynamicForm.watch = {}; +// Maak een Vue mixin die iconManager toevoegt aan elk component dat het nodig heeft +export const IconManagerMixin = { + created() { + // Check of er een formData.icon property is + if (this.formData && this.formData.icon) { + window.iconManager.loadIcon(this.formData.icon); } + }, - window.DynamicForm.watch['formData.icon'] = { - handler: function(newIcon) { - if (newIcon) { - window.iconManager.loadIcon(newIcon); - } - }, - immediate: true - }; + // Watch voor formData.icon veranderingen + watch: { + 'formData.icon': function(newIcon) { + if (newIcon) { + window.iconManager.loadIcon(newIcon); + } + } + }, - console.log('DynamicForm is uitgebreid met iconManager functionaliteit'); - } else { - console.warn('DynamicForm component is niet beschikbaar. iconManager kan niet worden toegevoegd.'); + // Methode om toe te voegen aan componenten + methods: { + loadIcon(iconName, options) { + window.iconManager.loadIcon(iconName, options); + }, + + loadIcons(iconNames, options) { + window.iconManager.loadIcons(iconNames, options); + } } -} +}; -// Probeer het DynamicForm component te initialiseren zodra het document geladen is -document.addEventListener('DOMContentLoaded', function() { - // Wacht een korte tijd om er zeker van te zijn dat DynamicForm is geladen - setTimeout(initDynamicFormWithIcons, 100); -}); - -// Als DynamicForm al beschikbaar is, initialiseer direct -if (window.DynamicForm) { - initDynamicFormWithIcons(); -} +// We hoeven niet langer DynamicForm te manipuleren +// omdat Vue componenten nu de IconManagerMixin kunnen gebruiken +console.log('IconManager en IconManagerMixin zijn beschikbaar voor componenten'); diff --git a/eveai_chat_client/templates/base.html b/eveai_chat_client/templates/base.html index fef8ecc..8ba434c 100644 --- a/eveai_chat_client/templates/base.html +++ b/eveai_chat_client/templates/base.html @@ -36,7 +36,7 @@ {{ tenant_make.name|default('') }}
- + @@ -47,44 +47,7 @@ - - - + {% include 'scripts.html' %} {% block scripts %}{% endblock %} diff --git a/eveai_chat_client/templates/chat.html b/eveai_chat_client/templates/chat.html index 642c8e6..183b58b 100644 --- a/eveai_chat_client/templates/chat.html +++ b/eveai_chat_client/templates/chat.html @@ -4,10 +4,8 @@ {% block title %}{{ tenant_make.name|default('EveAI') }} - AI Chat{% endblock %} {% block head %} - - - + + + {% endblock %} diff --git a/eveai_chat_client/templates/scripts.html b/eveai_chat_client/templates/scripts.html new file mode 100644 index 0000000..af29df7 --- /dev/null +++ b/eveai_chat_client/templates/scripts.html @@ -0,0 +1,33 @@ + + + + + + + diff --git a/frontend_src/js/chat-client.js b/frontend_src/js/chat-client.js new file mode 100644 index 0000000..cded11e --- /dev/null +++ b/frontend_src/js/chat-client.js @@ -0,0 +1,72 @@ +// Chat Client JavaScript modules +// Modern gebundelde versie met ES modules + +// CSS imports - zorg dat deze bestanden bestaan en correct worden gebundeld door Parcel +import '../css/chat-client.css'; +// CSS imports uit eveai_chat_client +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'; +import '../../../eveai_chat_client/static/assets/css/language-selector.css'; + +// Dependencies +import { createApp } from 'vue'; +import { marked } from 'marked'; + +// Maak fundamentele libraries globaal beschikbaar +window.Vue = { createApp }; +window.marked = marked; + +// Support tools +import iconManager, { IconManagerMixin } from '../../../eveai_chat_client/static/assets/js/iconManager.js'; +import '../../../eveai_chat_client/static/assets/js/translation.js'; + +// Gebruik barrel export voor componenten +import * as Components from '../../../eveai_chat_client/static/assets/js/components/index.js'; + +// Maak Components globaal beschikbaar voor debugging +window.Components = Components; +console.log('Components loaded:', Object.keys(Components)); + +// Main chat application - moet als laatste worden geladen +import { ChatApp } from '../../../eveai_chat_client/static/assets/js/ChatApp.js'; + +// Wacht tot het document volledig is geladen voordat we Vue initialiseren +document.addEventListener('DOMContentLoaded', () => { + console.log('Initializing Chat Application'); + + // Check of #app element bestaat + const appElement = document.getElementById('app'); + if (!appElement) { + console.error('DOM element #app not found. Cannot initialize Vue application.'); + return; + } + + console.log('DOM element #app exists:', appElement); + + try { + // Maak de Vue applicatie aan + const app = createApp(ChatApp); + + // Registreer alle componenten globaal + for (const [name, component] of Object.entries(Components)) { + app.component(name, component); + } + + // Voeg de IconManagerMixin toe voor alle componenten + app.mixin(IconManagerMixin); + + // Mount de applicatie op #app + const mountedApp = app.mount('#app'); + + // Bewaar een referentie naar de app voor debugging + window.__vueApp = app; + + console.log('Vue app mounted successfully'); + } catch (error) { + console.error('Error initializing Vue application:', error); + } +}); diff --git a/nginx/frontend_src/js/chat-client.js b/nginx/frontend_src/js/chat-client.js index 942384e..2b100a1 100644 --- a/nginx/frontend_src/js/chat-client.js +++ b/nginx/frontend_src/js/chat-client.js @@ -1,8 +1,5 @@ -// Chat Client JavaScript modules -// Importeer alle chat client componenten - +// 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'; @@ -11,29 +8,295 @@ import '../../../eveai_chat_client/static/assets/css/form.css'; import '../../../eveai_chat_client/static/assets/css/form-message.css'; import '../../../eveai_chat_client/static/assets/css/language-selector.css'; -import { createApp } from 'vue'; -window.Vue = { createApp }; - +// Dependencies +import { createApp, version } from 'vue'; import { marked } from 'marked'; +import { FormField } from '../../../eveai_chat_client/static/assets/js/components/FormField.js'; + +// Vue en andere bibliotheken beschikbaar maken +window.Vue = { createApp, version }; window.marked = marked; +// Debug: Check Vue build type +console.log('πŸ” [DEBUG] Vue object:', window.Vue); +console.log('πŸ” [DEBUG] Vue.createApp:', typeof window.Vue.createApp); +console.log('πŸ” [DEBUG] Vue.version:', window.Vue.version); - -// Main chat application -import '../../../eveai_chat_client/static/assets/js/chat-app.js'; +// Support tools import '../../../eveai_chat_client/static/assets/js/iconManager.js'; import '../../../eveai_chat_client/static/assets/js/translation.js'; -// Chat components -import '../../../eveai_chat_client/static/assets/js/components/ChatInput.js'; -import '../../../eveai_chat_client/static/assets/js/components/ChatMessage.js'; -import '../../../eveai_chat_client/static/assets/js/components/DynamicForm.js'; -import '../../../eveai_chat_client/static/assets/js/components/FormField.js'; -import '../../../eveai_chat_client/static/assets/js/components/FormMessage.js'; -import '../../../eveai_chat_client/static/assets/js/components/LanguageSelector.js'; -import '../../../eveai_chat_client/static/assets/js/components/MaterialIconManager.js'; -import '../../../eveai_chat_client/static/assets/js/components/MessageHistory.js'; -import '../../../eveai_chat_client/static/assets/js/components/ProgressTracker.js'; -import '../../../eveai_chat_client/static/assets/js/components/TypingIndicator.js'; +// Gebruik barrel export voor componenten +import * as Components from '../../../eveai_chat_client/static/assets/js/components/index.js'; -console.log('Chat client modules geladen en gebundeld.'); +// 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/js/components/LanguageSelector.js'; +import { ChatApp } from '../../../eveai_chat_client/static/assets/js/ChatApp.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() { + console.log('πŸ” [DEBUG] DOM content loaded, initializing application...'); + + // Controleer of chatConfig is ingesteld + if (!window.chatConfig) { + console.error('chatConfig is niet beschikbaar'); + window.chatConfig = {}; + } + + // Vul de sidebar explanation in + fillSidebarExplanation(); + + // Initialiseer language selector + initializeLanguageSelector(); + + // Initialiseer chat app (simpel) + initializeChatApp(); +}); + +/** + * Vul de sidebar explanation in + */ +function fillSidebarExplanation() { + const sidebarElement = document.getElementById('sidebar-explanation'); + if (sidebarElement && window.chatConfig.explanation) { + if (typeof window.marked === 'function') { + sidebarElement.innerHTML = window.marked(window.chatConfig.explanation); + } else if (window.marked && typeof window.marked.parse === 'function') { + sidebarElement.innerHTML = window.marked.parse(window.chatConfig.explanation.replace(/\[\[(.*?)\]\]/g, '$1')); + } else { + sidebarElement.innerHTML = window.chatConfig.explanation; + } + } +} + +/** + * Initialiseert de language selector + */ +function initializeLanguageSelector() { + console.log('πŸ” [DEBUG] Start initializeLanguageSelector'); + + const container = document.getElementById('language-selector-container'); + console.log('πŸ” [DEBUG] Container element:', container); + + if (!container) { + console.error('#language-selector-container niet gevonden'); + return; + } + + try { + // Maak props voor de component + const props = { + initialLanguage: window.chatConfig.language || 'nl', + supportedLanguageDetails: window.chatConfig.supportedLanguageDetails || {}, + allowedLanguages: window.chatConfig.allowedLanguages || ['nl', 'en', 'fr', 'de'] + }; + + console.log('πŸ” [DEBUG] Props voor LanguageSelector:', JSON.stringify(props, null, 2)); + + // Mount de component direct - BELANGRIJK: we gebruiken window.Vue als dat beschikbaar is + // Dit is nodig voor compatibiliteit met oude code + const app = window.Vue && typeof window.Vue.createApp === 'function' + ? window.Vue.createApp(LanguageSelector, props) + : createApp(LanguageSelector, props); + + // Registreer error handler + app.config.errorHandler = (err, vm, info) => { + console.error('🚨 [Vue Error]', err); + console.error('Component:', vm); + console.error('Error Info:', info); + }; + + try { + // Mount de component + const mountedApp = app.mount(container); + console.log('πŸ” [DEBUG] LanguageSelector instantie gemount:', mountedApp); + } catch (mountError) { + console.error('🚨 [ERROR] Fout bij mounten van LanguageSelector met Vue:', mountError); + console.log('πŸ” [DEBUG] Probeer alternatieve direct DOM rendering...'); + + // Probeer de legacy renderComponent methode als Vue mounting mislukt + if (typeof LanguageSelector.renderComponent === 'function') { + console.log('πŸ” [DEBUG] LanguageSelector.renderComponent gevonden, directe rendering uitvoeren'); + try { + LanguageSelector.renderComponent(container, props); + console.log('πŸ” [DEBUG] LanguageSelector direct gerenderd via renderComponent'); + } catch (renderError) { + console.error('🚨 [ERROR] Ook alternatieve rendering gefaald:', renderError); + } + } else { + console.error('🚨 [ERROR] Geen renderComponent methode beschikbaar op LanguageSelector'); + + // Noodoplossing: Handmatige DOM manipulatie + container.innerHTML = ` +
+ +
+ +
+
+ `; + + // Event listener voor de fallback oplossing + const selectElement = container.querySelector('#language-select'); + if (selectElement) { + selectElement.addEventListener('change', (e) => { + const newLanguage = e.target.value; + console.log(`Taal gewijzigd naar ${newLanguage} (via fallback)`); + + // Update chatConfig + if (window.chatConfig) { + window.chatConfig.language = newLanguage; + } + + // Stuur event voor andere componenten + const globalEvent = new CustomEvent('language-changed', { + detail: { language: newLanguage } + }); + document.dispatchEvent(globalEvent); + + // Sla voorkeur op + localStorage.setItem('preferredLanguage', newLanguage); + }); + } + } + } + + // Language change event listener + document.addEventListener('vue:language-changed', function(event) { + const newLanguage = event.detail.language; + console.log(`Taal gewijzigd naar ${newLanguage}`); + + // Update chatConfig + if (window.chatConfig) { + window.chatConfig.language = newLanguage; + } + + // Stuur event voor andere componenten + const globalEvent = new CustomEvent('language-changed', { + detail: { language: newLanguage } + }); + document.dispatchEvent(globalEvent); + + // Sla voorkeur op + localStorage.setItem('preferredLanguage', newLanguage); + }); + + console.log('πŸ” [DEBUG] Language selector setup voltooid'); + } catch (error) { + console.error('🚨 [CRITICAL ERROR] Bij initialiseren language selector:', error); + console.error('Stack trace:', error.stack); + } +} + +/** + * Initialiseert de chat app (Vue component) + */ +function initializeChatApp() { + console.log('πŸ” [DEBUG] Start initializeChatApp'); + + const container = document.querySelector('.chat-container'); + console.log('πŸ” [DEBUG] Chat container element:', container); + + if (!container) { + console.error('.chat-container niet gevonden'); + return; + } + + try { + // Controleer of componenten beschikbaar zijn en log debug informatie + console.log('πŸ” [DEBUG] ChatApp component beschikbaar:', ChatApp); + console.log('πŸ” [DEBUG] GeΓ―mporteerde componenten:', Object.keys(Components)); + console.log('πŸ” [DEBUG] MessageHistory component:', Components.MessageHistory); + console.log('πŸ” [DEBUG] ChatInput component:', Components.ChatInput); + + // Geen workarounds voor Popper nodig + + // Controleer of renderComponent methoden beschikbaar zijn + console.log('πŸ” [DEBUG] MessageHistory.renderComponent beschikbaar:', + typeof Components.MessageHistory.renderComponent === 'function'); + console.log('πŸ” [DEBUG] ChatInput.renderComponent beschikbaar:', + typeof Components.ChatInput.renderComponent === 'function'); + + if (!ChatApp) { + throw new 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'] + }; + + console.log('πŸ” [DEBUG] Alle componenten registreren voor ChatApp...'); + + console.log('πŸ” [DEBUG] Props voor ChatApp:', JSON.stringify(props, null, 2)); + + // Mount de component met alle nodige componenten + const app = createApp(ChatApp, props); + + // Registreer alle componenten globaal + Object.entries(Components).forEach(([name, component]) => { + console.log(`πŸ” [DEBUG] Registreer component globaal: ${name}`); + 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); + }; + + // Log app object voor debugging + console.log('πŸ” [DEBUG] ChatApp Vue app object:', app); + console.log('πŸ” [DEBUG] App.mount functie:', typeof app.mount); + + // Mount de component + const mountedApp = app.mount(container); + console.log('πŸ” [DEBUG] ChatApp gemount, instance:', mountedApp); + + // Bewaar referentie globaal voor debugging + window.__chatApp = mountedApp; + + // Bewaar een referentie naar de Vue instantie voor gebruik door andere componenten + window.__vueApp = app; + console.log('πŸ” [DEBUG] Vue app instance globaal beschikbaar als window.__vueApp'); + + // Log belangrijke methods van de gemounte component + console.log('πŸ” [DEBUG] Belangrijke ChatApp methods beschikbaar:', { + renderComponent: typeof mountedApp.renderComponent === 'function', + initializeChat: typeof mountedApp.initializeChat === 'function' + }); + } catch (error) { + console.error('🚨 [CRITICAL ERROR] Bij initialiseren chat app:', error); + console.error('Stack trace:', error.stack); + + // Fallback naar placeholder + container.innerHTML = '
Fout bij het initialiseren van de chat applicatie
'; + } +} + +console.log('Chat client modules geladen en gebundeld met moderne ES module structuur.'); \ No newline at end of file diff --git a/nginx/package.json b/nginx/package.json index 6217a0a..483b377 100644 --- a/nginx/package.json +++ b/nginx/package.json @@ -20,6 +20,18 @@ "@parcel/transformer-sass": "^2.15.2", "parcel": "^2.15.2" }, + "alias": { + "vue": "vue/dist/vue.esm-bundler.js" + }, + "browserslist": [ + "> 0.5%", + "last 2 versions", + "not dead" + ], + "@parcel/transformer-js": { + "inlineFS": true, + "inlineEnvironment": ["NODE_ENV"] + }, "scripts": { "prebuild": "mkdir -p static/dist && cp -r ../eveai_app/static/assets static/", "build": "npm run prebuild && npm run build:main && npm run build:chat",