- Eerste stap in het opnieuw laten werken van de chat client...

This commit is contained in:
Josako
2025-07-18 16:07:13 +02:00
parent f3a243698c
commit 11b1d548bd
20 changed files with 1201 additions and 352 deletions

View File

@@ -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, '<strong>$1</strong>'));
} 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 = `
<div class="chat-app-container">
<!-- Message History - takes available space -->
<div id="chat-message-history" class="chat-messages-area"></div>
<!-- Chat Input - to the bottom -->
<div id="chat-input-container" class="chat-input-area"></div>
</div>
`;
// 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 = {
</div>
`
};
// 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: `
<language-selector
:initial-language="currentLanguage"
:supported-language-details="supportedLanguageDetails"
:allowed-languages="allowedLanguages"
@language-changed="handleLanguageChange"
></language-selector>
`
});
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);
});

View File

@@ -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 = `<div class="chat-input-loading">Chat input laden...</div>`;
// 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: `
<div class="chat-input-container">
<!-- Dynamisch toevoegen van Material Symbols Outlined voor iconen -->
<div v-if="formData && formData.icon" class="material-icons-container">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
</div>
<!-- Material Icons worden nu globaal geladen in scripts.html -->
<!-- Dynamisch formulier container -->
<div v-if="formData" class="dynamic-form-container">
<!-- De titel wordt in DynamicForm weergegeven en niet hier om dubbele titels te voorkomen -->

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 =>
`<option value="${lang.code}" ${lang.code === this.selectedLanguage ? 'selected' : ''}>${lang.flag} ${lang.name}</option>`
).join('');
container.innerHTML = `
<div class="language-selector">
<label for="language-select">Taal / Language:</label>
<div class="select-wrapper">
<select id="language-select" class="language-select">
${optionsHtml}
</select>
</div>
</div>
`;
// 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: `
<div class="language-selector">
<label for="language-select">Taal / Language:</label>
<div class="select-wrapper">
<select
id="language-select"
v-model="selectedLanguage"
@change="changeLanguage(selectedLanguage); currentLanguage = selectedLanguage"
class="language-select"
>
<option
v-for="lang in availableLanguages"
:key="lang.code"
:value="lang.code"
>
{{ lang.flag }} {{ lang.name }}
</option>
<select id="language-select" class="language-select" v-model="selectedLanguage" @change="changeLanguage(selectedLanguage)">
<option v-for="lang in getAvailableLanguages()" :key="lang.code" :value="lang.code">{{ lang.flag }} {{ lang.name }}</option>
</select>
</div>
</div>
`
`,
// Minimale render functie als fallback
render() {
return document.createElement('div');
}
};

View File

@@ -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 = {
<typing-indicator v-if="isTyping"></typing-indicator>
</div>
</div>
`,
`
};
// 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;
}
};

View File

@@ -308,4 +308,26 @@ export const ProgressTracker = {
</div>
</div>
`
};
// 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;
}
};

View File

@@ -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: `
<div class="typing-indicator">
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div class="typing-dot"></div>
<div v-if="showText" class="typing-text">{{ text }}</div>
</div>
`
};
// 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;
}
};

View File

@@ -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';

View File

@@ -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');