- Re-introduction of EveAIAsset - Make translation services resistent for situation with and without current_event defined. - Ensure first question is asked in eveai_chat_client - Start of version 1.4.0 of TRAICIE_SELECTION_SPECIALIST
935 lines
35 KiB
JavaScript
935 lines
35 KiB
JavaScript
// Import all components
|
|
import { TypingIndicator } from '/static/assets/js/components/TypingIndicator.js';
|
|
import { FormField } from '/static/assets/js/components/FormField.js';
|
|
import { DynamicForm } from '/static/assets/js/components/DynamicForm.js';
|
|
import { ChatMessage } from '/static/assets/js/components/ChatMessage.js';
|
|
import { MessageHistory } from '/static/assets/js/components/MessageHistory.js';
|
|
import { ProgressTracker } from '/static/assets/js/components/ProgressTracker.js';
|
|
import { LanguageSelector } from '/static/assets/js/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 '/static/assets/js/components/ChatInput.js';
|
|
|
|
// Main Chat Application
|
|
export const ChatApp = {
|
|
name: 'ChatApp',
|
|
components: {
|
|
TypingIndicator,
|
|
FormField,
|
|
DynamicForm,
|
|
ChatMessage,
|
|
MessageHistory,
|
|
ChatInput
|
|
},
|
|
|
|
data() {
|
|
// Maak een lokale kopie van de chatConfig om undefined errors te voorkomen
|
|
const chatConfig = window.chatConfig || {};
|
|
const settings = chatConfig.settings || {};
|
|
const initialLanguage = chatConfig.language || 'nl';
|
|
const originalExplanation = chatConfig.explanation || '';
|
|
|
|
return {
|
|
// Taal gerelateerde data
|
|
currentLanguage: '',
|
|
supportedLanguageDetails: chatConfig.supportedLanguageDetails || {},
|
|
allowedLanguages: chatConfig.allowedLanguages || ['nl', 'en', 'fr', 'de'],
|
|
originalExplanation: originalExplanation,
|
|
explanation: chatConfig.explanation || '',
|
|
|
|
// Chat-specific data
|
|
currentMessage: '',
|
|
allMessages: [],
|
|
isTyping: false,
|
|
isLoading: false,
|
|
isSubmittingForm: false,
|
|
messageIdCounter: 1,
|
|
formValues: {},
|
|
currentInputFormData: null,
|
|
|
|
// API prefix voor endpoints
|
|
apiPrefix: chatConfig.apiPrefix || '',
|
|
|
|
// Configuration from Flask/server
|
|
conversationId: chatConfig.conversationId || 'default',
|
|
userId: chatConfig.userId || null,
|
|
userName: chatConfig.userName || '',
|
|
|
|
// Settings met standaard waarden en overschreven door server config
|
|
settings: {
|
|
maxMessageLength: settings.maxMessageLength || 2000,
|
|
allowFileUpload: settings.allowFileUpload === true,
|
|
allowVoiceMessage: settings.allowVoiceMessage === true,
|
|
autoScroll: settings.autoScroll === true
|
|
},
|
|
|
|
// UI state
|
|
isMobile: window.innerWidth <= 768,
|
|
showSidebar: window.innerWidth > 768,
|
|
|
|
// Advanced features
|
|
messageSearch: '',
|
|
filteredMessages: [],
|
|
isSearching: false
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
// Keep existing computed from base.html
|
|
compiledExplanation() {
|
|
if (typeof marked === 'function') {
|
|
return marked(this.explanation);
|
|
} else if (marked && typeof marked.parse === 'function') {
|
|
return marked.parse(this.explanation);
|
|
} else {
|
|
console.error('Marked library not properly loaded');
|
|
return this.explanation;
|
|
}
|
|
},
|
|
|
|
displayMessages() {
|
|
return this.isSearching ? this.filteredMessages : this.allMessages;
|
|
},
|
|
|
|
hasMessages() {
|
|
return this.allMessages.length > 0;
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
this.initializeChat();
|
|
this.setupEventListeners();
|
|
},
|
|
|
|
beforeUnmount() {
|
|
this.cleanup();
|
|
},
|
|
|
|
methods: {
|
|
// Initialization
|
|
initializeChat() {
|
|
console.log('Initializing chat application...');
|
|
|
|
// Load historical messages from server
|
|
this.loadHistoricalMessages();
|
|
|
|
console.log('Nr of messages:', this.allMessages.length);
|
|
|
|
// Add welcome message if no history
|
|
if (this.allMessages.length === 0) {
|
|
this.addWelcomeMessage();
|
|
}
|
|
|
|
// Focus input after initialization
|
|
this.$nextTick(() => {
|
|
this.focusChatInput();
|
|
});
|
|
},
|
|
|
|
loadHistoricalMessages() {
|
|
// Veilige toegang tot messages met fallback
|
|
const chatConfig = window.chatConfig || {};
|
|
const historicalMessages = chatConfig.messages || [];
|
|
|
|
if (historicalMessages.length > 0) {
|
|
this.allMessages = historicalMessages
|
|
.filter(msg => msg !== null && msg !== undefined) // Filter null/undefined berichten uit
|
|
.map(msg => {
|
|
// Zorg voor een correct geformatteerde bericht-object
|
|
return {
|
|
id: this.messageIdCounter++,
|
|
content: typeof msg === 'string' ? msg : (msg.content || ''),
|
|
sender: msg.sender || 'ai',
|
|
type: msg.type || 'text',
|
|
timestamp: msg.timestamp || new Date().toISOString(),
|
|
formData: msg.formData || null,
|
|
status: msg.status || 'delivered'
|
|
};
|
|
});
|
|
|
|
console.log(`Loaded ${this.allMessages.length} historical messages`);
|
|
}
|
|
},
|
|
|
|
async addWelcomeMessage() {
|
|
console.log('Sending initialize message to backend');
|
|
|
|
// Toon typing indicator
|
|
this.isTyping = true;
|
|
this.isLoading = true;
|
|
|
|
try {
|
|
// Verzamel gegevens voor de API call
|
|
const apiData = {
|
|
message: 'Initialize',
|
|
conversation_id: this.conversationId,
|
|
user_id: this.userId,
|
|
language: this.currentLanguage
|
|
};
|
|
|
|
const response = await this.callAPI('/api/send_message', apiData);
|
|
|
|
// Verberg typing indicator
|
|
this.isTyping = false;
|
|
|
|
// Voeg AI response toe met task_id voor tracking
|
|
const aiMessage = this.addMessage(
|
|
'',
|
|
'ai',
|
|
'text'
|
|
);
|
|
|
|
// Voeg task_id toe als beschikbaar
|
|
if (response.task_id) {
|
|
console.log('Monitoring Initialize Task ID: ', response.task_id);
|
|
aiMessage.taskId = response.task_id;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error sending initialize message:', error);
|
|
this.isTyping = false;
|
|
|
|
// Voeg standaard welkomstbericht toe als fallback
|
|
this.addMessage(
|
|
'Hallo! Ik ben je AI assistant. Vraag gerust om een formulier zoals "contactformulier" of "bestelformulier"!',
|
|
'ai',
|
|
'text'
|
|
);
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
|
|
setupEventListeners() {
|
|
// Window resize listener
|
|
window.addEventListener('resize', this.handleResize);
|
|
|
|
// Keyboard shortcuts
|
|
document.addEventListener('keydown', this.handleGlobalKeydown);
|
|
|
|
// Luister naar taalwijzigingen via custom events
|
|
document.addEventListener('language-changed', (event) => {
|
|
if (event.detail && event.detail.language) {
|
|
console.log('ChatApp received language-changed event:', event.detail.language);
|
|
this.handleLanguageChange(event.detail.language);
|
|
}
|
|
});
|
|
},
|
|
|
|
cleanup() {
|
|
window.removeEventListener('resize', this.handleResize);
|
|
document.removeEventListener('keydown', this.handleGlobalKeydown);
|
|
},
|
|
|
|
// Taal gerelateerde functionaliteit
|
|
handleLanguageChange(newLanguage) {
|
|
if (this.currentLanguage !== newLanguage) {
|
|
console.log(`ChatApp: Taal gewijzigd van ${this.currentLanguage} naar ${newLanguage}`);
|
|
this.currentLanguage = newLanguage;
|
|
|
|
// Vertaal de sidebar
|
|
this.translateSidebar(newLanguage);
|
|
|
|
// Sla de taalvoorkeur op voor toekomstige API calls
|
|
this.storeLanguagePreference(newLanguage);
|
|
|
|
// Stuur language-changed event voor andere componenten (zoals ChatInput)
|
|
// Dit wordt gedaan via het event systeem, waardoor we geen directe referentie nodig hebben
|
|
const event = new CustomEvent('language-changed', {
|
|
detail: { language: newLanguage }
|
|
});
|
|
document.dispatchEvent(event);
|
|
}
|
|
},
|
|
|
|
// Maak de handleLanguageChange methode toegankelijk van buitenaf
|
|
// Deze functie wordt opgeroepen door het externe LanguageSelector component
|
|
__handleExternalLanguageChange(newLanguage) {
|
|
this.handleLanguageChange(newLanguage);
|
|
},
|
|
|
|
storeLanguagePreference(language) {
|
|
// Sla op in localStorage voor persistentie
|
|
localStorage.setItem('preferredLanguage', language);
|
|
|
|
// Update chatConfig voor toekomstige API calls
|
|
if (window.chatConfig) {
|
|
window.chatConfig.language = language;
|
|
}
|
|
|
|
console.log(`Taalvoorkeur opgeslagen: ${language}`);
|
|
},
|
|
|
|
async translateSidebar(language) {
|
|
console.log(`Sidebar wordt vertaald naar: ${language}`);
|
|
|
|
// Haal de originele tekst op
|
|
const originalText = this.originalExplanation || this.explanation;
|
|
|
|
try {
|
|
// Controleer of TranslationClient beschikbaar is
|
|
if (!window.TranslationClient || typeof window.TranslationClient.translate !== 'function') {
|
|
console.error('TranslationClient.translate is niet beschikbaar');
|
|
this.showTranslationIndicator(language, 'Vertaling niet beschikbaar', false);
|
|
return;
|
|
}
|
|
|
|
// Toon loading indicator
|
|
this.showTranslationIndicator(language, 'Bezig met vertalen...');
|
|
console.log('API prefix voor vertaling:', this.apiPrefix);
|
|
|
|
// Gebruik TranslationClient met de juiste parameters
|
|
const response = await window.TranslationClient.translate(
|
|
originalText,
|
|
language,
|
|
null, // source_lang (auto-detect)
|
|
'sidebar_explanation', // context
|
|
this.apiPrefix // API prefix voor tenant routing
|
|
);
|
|
|
|
if (response.success) {
|
|
// Update de explanation variabele
|
|
console.log('Translated text: ' + response.translated_text);
|
|
this.explanation = response.translated_text;
|
|
|
|
// 1. Update de Vue instance
|
|
if (window.__vueApp && window.__vueApp._instance) {
|
|
window.__vueApp._instance.proxy.explanation = response.translated_text;
|
|
}
|
|
|
|
// 2. Update direct het DOM-element via marked voor onmiddellijke weergave
|
|
const sidebarElement = document.querySelector('.sidebar-explanation');
|
|
if (sidebarElement) {
|
|
console.log('DOM-element gevonden, directe update toepassen');
|
|
// Gebruik de marked library om de markdown naar HTML te converteren
|
|
let htmlContent;
|
|
if (typeof marked === 'function') {
|
|
htmlContent = marked(response.translated_text);
|
|
} else if (marked && typeof marked.parse === 'function') {
|
|
htmlContent = marked.parse(response.translated_text);
|
|
} else {
|
|
htmlContent = response.translated_text;
|
|
}
|
|
|
|
// Update de inhoud direct
|
|
sidebarElement.innerHTML = htmlContent;
|
|
} else {
|
|
console.error('Sidebar explanation element niet gevonden in DOM');
|
|
}
|
|
|
|
this.showTranslationIndicator(language, 'Vertaling voltooid!', true);
|
|
} else {
|
|
console.error('Vertaling mislukt:', response.error);
|
|
this.showTranslationIndicator(language, 'Vertaling mislukt', false);
|
|
}
|
|
} catch (error) {
|
|
console.error('Fout bij vertalen sidebar:', error);
|
|
this.showTranslationIndicator(language, 'Vertaling mislukt', false);
|
|
}
|
|
},
|
|
|
|
// Message management
|
|
addMessage(content, sender, type = 'text', formData = null, formValues = null) {
|
|
const message = {
|
|
id: this.messageIdCounter++,
|
|
content,
|
|
sender,
|
|
type,
|
|
formData,
|
|
formValues,
|
|
timestamp: new Date().toISOString(),
|
|
status: sender === 'user' ? 'sent' : 'delivered'
|
|
};
|
|
|
|
this.allMessages.push(message);
|
|
|
|
// Initialize form values if it's a form and no values were provided
|
|
if (type === 'form' && formData && !formValues) {
|
|
// Vue 3 compatibele manier om reactieve objecten bij te werken
|
|
this.formValues[message.id] = {};
|
|
formData.fields.forEach(field => {
|
|
const fieldName = field.name || field.id;
|
|
if (fieldName) {
|
|
this.formValues[message.id][fieldName] = field.defaultValue || '';
|
|
}
|
|
});
|
|
}
|
|
|
|
// Update search results if searching
|
|
if (this.isSearching) {
|
|
this.performSearch();
|
|
}
|
|
|
|
return message;
|
|
},
|
|
|
|
showTranslationIndicator(language, message, success = null) {
|
|
const explanationElement = document.querySelector('.sidebar-explanation');
|
|
if (explanationElement) {
|
|
// Verwijder eventuele bestaande indicators
|
|
const existingIndicator = explanationElement.querySelector('.language-change-indicator');
|
|
if (existingIndicator) {
|
|
existingIndicator.remove();
|
|
}
|
|
|
|
// Voeg nieuwe indicator toe
|
|
const indicator = document.createElement('div');
|
|
indicator.className = 'language-change-indicator';
|
|
if (success === true) indicator.classList.add('success');
|
|
if (success === false) indicator.classList.add('error');
|
|
|
|
indicator.innerHTML = `<small>${message}</small>`;
|
|
explanationElement.prepend(indicator);
|
|
|
|
// Verwijder na 3 seconden, behalve bij loading
|
|
if (success !== null) {
|
|
setTimeout(() => {
|
|
if (explanationElement.contains(indicator)) {
|
|
indicator.remove();
|
|
}
|
|
}, 3000);
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
// Helper functie om formulierdata toe te voegen aan bestaande berichten
|
|
attachFormDataToMessage(messageId, formData, formValues) {
|
|
const message = this.allMessages.find(m => m.id === messageId);
|
|
if (message) {
|
|
message.formData = formData;
|
|
message.formValues = formValues;
|
|
}
|
|
},
|
|
|
|
updateCurrentMessage(value) {
|
|
this.currentMessage = value;
|
|
},
|
|
|
|
// Message sending (alleen voor gewone tekstberichten, geen formulieren)
|
|
async sendMessage() {
|
|
const text = this.currentMessage.trim();
|
|
|
|
// Controleer of we kunnen verzenden
|
|
if (!text || this.isLoading) return;
|
|
|
|
console.log('Sending text message:', text);
|
|
|
|
// Add user message
|
|
const userMessage = this.addMessage(text, 'user', 'text');
|
|
|
|
// Wis input
|
|
this.currentMessage = '';
|
|
|
|
// Show typing and loading state
|
|
this.isTyping = true;
|
|
this.isLoading = true;
|
|
|
|
try {
|
|
// Verzamel gegevens voor de API call
|
|
const apiData = {
|
|
message: text,
|
|
conversation_id: this.conversationId,
|
|
user_id: this.userId,
|
|
language: this.currentLanguage
|
|
};
|
|
|
|
const response = await this.callAPI('/api/send_message', apiData);
|
|
|
|
// Hide typing indicator
|
|
this.isTyping = false;
|
|
|
|
// Mark user message as delivered
|
|
userMessage.status = 'delivered';
|
|
|
|
// Add AI response
|
|
if (response.type === 'form') {
|
|
this.addMessage('', 'ai', 'form', response.formData);
|
|
} else {
|
|
// Voeg het bericht toe met task_id voor tracking - initieel leeg
|
|
const aiMessage = this.addMessage(
|
|
'',
|
|
'ai',
|
|
'text'
|
|
);
|
|
|
|
// Voeg task_id toe als beschikbaar
|
|
if (response.task_id) {
|
|
console.log('Monitoring Task ID: ', response.task_id);
|
|
aiMessage.taskId = response.task_id;
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Error sending message:', error);
|
|
this.isTyping = false;
|
|
|
|
// Mark user message as failed
|
|
userMessage.status = 'failed';
|
|
|
|
this.addMessage(
|
|
'Sorry, er ging iets mis bij het verzenden van je bericht. Probeer het opnieuw.',
|
|
'ai',
|
|
'error'
|
|
);
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
|
|
async submitFormFromInput(formValues) {
|
|
this.isSubmittingForm = true;
|
|
|
|
if (!this.currentInputFormData) {
|
|
console.error('No form data available');
|
|
return;
|
|
}
|
|
|
|
console.log('Form values received:', formValues);
|
|
console.log('Current input form data:', this.currentInputFormData);
|
|
|
|
try {
|
|
// Maak een user message met formuliergegevens én eventuele tekst
|
|
const userMessage = this.addMessage(
|
|
this.currentMessage.trim(), // Voeg tekst toe als die er is
|
|
'user',
|
|
'text'
|
|
);
|
|
|
|
// Voeg formuliergegevens toe aan het bericht
|
|
userMessage.formData = this.currentInputFormData;
|
|
userMessage.formValues = formValues;
|
|
|
|
// Reset het tekstbericht
|
|
this.currentMessage = '';
|
|
this.$emit('update-message', '');
|
|
|
|
// Toon laad-indicator
|
|
this.isTyping = true;
|
|
this.isLoading = true;
|
|
|
|
// Verzamel gegevens voor de API call
|
|
const apiData = {
|
|
message: userMessage.content,
|
|
conversation_id: this.conversationId,
|
|
user_id: this.userId,
|
|
form_values: formValues // Voeg formuliergegevens toe aan API call
|
|
};
|
|
|
|
// Verstuur bericht naar de API
|
|
const response = await this.callAPI('/api/send_message', apiData);
|
|
|
|
// Verberg de typing indicator
|
|
this.isTyping = false;
|
|
|
|
// Markeer het gebruikersbericht als afgeleverd
|
|
userMessage.status = 'delivered';
|
|
|
|
// Voeg AI response toe met task_id voor tracking
|
|
const aiMessage = this.addMessage(
|
|
'',
|
|
'ai',
|
|
'text'
|
|
);
|
|
|
|
if (response.task_id) {
|
|
console.log('Monitoring Task ID: ', response.task_id);
|
|
aiMessage.taskId = response.task_id;
|
|
}
|
|
|
|
// Reset formulier na succesvolle verzending
|
|
this.currentInputFormData = null;
|
|
|
|
} catch (error) {
|
|
console.error('Error submitting form:', error);
|
|
this.addMessage(
|
|
'Sorry, er ging iets mis bij het verzenden van het formulier. Probeer het opnieuw.',
|
|
'ai',
|
|
'text'
|
|
);
|
|
// Wis ook hier het formulier na een fout
|
|
this.currentInputFormData = null;
|
|
} finally {
|
|
this.isSubmittingForm = false;
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
|
|
// Message actions
|
|
|
|
retryMessage(messageId) {
|
|
const message = this.allMessages.find(m => m.id === messageId);
|
|
if (message && message.status === 'failed') {
|
|
// Retry sending the message
|
|
this.currentMessage = message.content;
|
|
this.removeMessage(messageId);
|
|
this.sendMessage();
|
|
}
|
|
},
|
|
|
|
removeMessage(messageId) {
|
|
const index = this.allMessages.findIndex(m => m.id === messageId);
|
|
if (index !== -1) {
|
|
this.allMessages.splice(index, 1);
|
|
// Verwijder ook eventuele formuliergegevens
|
|
if (this.formValues[messageId]) {
|
|
delete this.formValues[messageId];
|
|
}
|
|
}
|
|
},
|
|
|
|
// File handling
|
|
async handleFileUpload(file) {
|
|
console.log('Uploading file:', file.name);
|
|
|
|
// Add file message
|
|
const fileMessage = this.addMessage('', 'user', 'file', {
|
|
fileName: file.name,
|
|
fileSize: this.formatFileSize(file.size),
|
|
fileType: file.type
|
|
});
|
|
|
|
try {
|
|
// TODO: Implement actual file upload
|
|
// const response = await this.uploadFile(file);
|
|
// fileMessage.fileUrl = response.url;
|
|
|
|
// Simulate file upload
|
|
setTimeout(() => {
|
|
fileMessage.fileUrl = URL.createObjectURL(file);
|
|
fileMessage.status = 'delivered';
|
|
}, 1000);
|
|
|
|
} catch (error) {
|
|
console.error('Error uploading file:', error);
|
|
fileMessage.status = 'failed';
|
|
}
|
|
},
|
|
|
|
async handleVoiceRecord(audioBlob) {
|
|
console.log('Processing voice recording');
|
|
|
|
// Add voice message
|
|
const voiceMessage = this.addMessage('', 'user', 'voice', {
|
|
audioBlob,
|
|
duration: '00:05' // TODO: Calculate actual duration
|
|
});
|
|
|
|
// TODO: Send to speech-to-text service
|
|
// const transcription = await this.transcribeAudio(audioBlob);
|
|
// this.currentMessage = transcription;
|
|
// this.sendMessage();
|
|
},
|
|
|
|
// Search functionality
|
|
performSearch() {
|
|
if (!this.messageSearch.trim()) {
|
|
this.isSearching = false;
|
|
this.filteredMessages = [];
|
|
return;
|
|
}
|
|
|
|
this.isSearching = true;
|
|
const query = this.messageSearch.toLowerCase();
|
|
|
|
this.filteredMessages = this.allMessages.filter(message =>
|
|
message.content &&
|
|
message.content.toLowerCase().includes(query)
|
|
);
|
|
},
|
|
|
|
clearSearch() {
|
|
this.messageSearch = '';
|
|
this.isSearching = false;
|
|
this.filteredMessages = [];
|
|
},
|
|
|
|
// Event handlers voor specialist events
|
|
handleSpecialistComplete(eventData) {
|
|
console.log('ChatApp received specialist-complete:', eventData);
|
|
|
|
// Als er een form_request is, toon deze in de ChatInput component
|
|
if (eventData.form_request) {
|
|
console.log('Setting form request in ChatInput:', eventData.form_request);
|
|
|
|
try {
|
|
// Converteer de form_request naar het verwachte formaat
|
|
const formData = this.convertFormRequest(eventData.form_request);
|
|
|
|
// Stel het formulier in als currentInputFormData in plaats van als bericht toe te voegen
|
|
if (formData && formData.title && formData.fields) {
|
|
this.currentInputFormData = formData;
|
|
} else {
|
|
console.error('Invalid form data after conversion:', formData);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error processing form request:', err);
|
|
}
|
|
}
|
|
},
|
|
|
|
handleSpecialistError(eventData) {
|
|
console.log('ChatApp received specialist-error:', eventData);
|
|
|
|
// Voeg foutbericht toe
|
|
this.addMessage(
|
|
eventData.message || 'Er is een fout opgetreden bij het verwerken van uw verzoek.',
|
|
'ai',
|
|
'error'
|
|
);
|
|
},
|
|
|
|
// Helper methode om form_request te converteren naar het verwachte formaat
|
|
convertFormRequest(formRequest) {
|
|
console.log('Converting form request:', formRequest);
|
|
|
|
if (!formRequest) {
|
|
console.error('Geen geldig formRequest ontvangen');
|
|
return null;
|
|
}
|
|
|
|
// Controleer of fields een object is voordat we converteren
|
|
let fieldsArray;
|
|
if (formRequest.fields && typeof formRequest.fields === 'object' && !Array.isArray(formRequest.fields)) {
|
|
// Converteer de fields van object naar array formaat
|
|
fieldsArray = Object.entries(formRequest.fields).map(([fieldId, fieldDef]) => ({
|
|
id: fieldId,
|
|
name: fieldDef.name || fieldId, // Gebruik fieldId als fallback
|
|
type: fieldDef.type || 'text', // Standaard naar text
|
|
description: fieldDef.description || '',
|
|
required: fieldDef.required || false,
|
|
default: fieldDef.default || '',
|
|
allowedValues: fieldDef.allowed_values || null,
|
|
context: fieldDef.context || null
|
|
}));
|
|
} else if (Array.isArray(formRequest.fields)) {
|
|
// Als het al een array is, zorg dat alle velden correct zijn
|
|
fieldsArray = formRequest.fields.map(field => ({
|
|
id: field.id || field.name,
|
|
name: field.name || field.id,
|
|
type: field.type || 'text',
|
|
description: field.description || '',
|
|
required: field.required || false,
|
|
default: field.default || field.defaultValue || '',
|
|
allowedValues: field.allowed_values || field.allowedValues || null,
|
|
context: field.context || null
|
|
}));
|
|
} else {
|
|
// Fallback naar lege array als er geen velden zijn
|
|
console.warn('Formulier heeft geen geldige velden');
|
|
fieldsArray = [];
|
|
}
|
|
|
|
return {
|
|
title: formRequest.name || formRequest.title || 'Formulier',
|
|
description: formRequest.description || '',
|
|
icon: formRequest.icon || 'form',
|
|
version: formRequest.version || '1.0',
|
|
fields: fieldsArray
|
|
};
|
|
},
|
|
|
|
// Event handlers
|
|
handleResize() {
|
|
this.isMobile = window.innerWidth <= 768;
|
|
this.showSidebar = window.innerWidth > 768;
|
|
},
|
|
|
|
handleGlobalKeydown(event) {
|
|
// Ctrl/Cmd + K for search
|
|
if ((event.ctrlKey || event.metaKey) && event.key === 'k') {
|
|
event.preventDefault();
|
|
this.focusSearch();
|
|
}
|
|
|
|
// Escape to clear search
|
|
if (event.key === 'Escape' && this.isSearching) {
|
|
this.clearSearch();
|
|
}
|
|
},
|
|
|
|
|
|
// Utility methods
|
|
async callAPI(endpoint, data) {
|
|
// Gebruik de API prefix uit de lokale variabele
|
|
const fullEndpoint = this.apiPrefix + '/chat' + endpoint;
|
|
|
|
console.log('Calling API with prefix:', {
|
|
prefix: this.apiPrefix,
|
|
endpoint: endpoint,
|
|
fullEndpoint: fullEndpoint
|
|
});
|
|
|
|
const response = await fetch(fullEndpoint, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(data)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
},
|
|
|
|
|
|
formatFileSize(bytes) {
|
|
if (bytes === 0) return '0 Bytes';
|
|
const k = 1024;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
},
|
|
|
|
focusChatInput() {
|
|
this.$refs.chatInput?.focusInput();
|
|
},
|
|
|
|
focusSearch() {
|
|
this.$refs.searchInput?.focus();
|
|
},
|
|
|
|
},
|
|
|
|
template: `
|
|
<div class="chat-app-container">
|
|
<!-- Message History - takes available space -->
|
|
<message-history
|
|
:messages="displayMessages"
|
|
:is-typing="isTyping"
|
|
:is-submitting-form="isSubmittingForm"
|
|
:api-prefix="apiPrefix"
|
|
:auto-scroll="true"
|
|
@specialist-error="handleSpecialistError"
|
|
@specialist-complete="handleSpecialistComplete"
|
|
ref="messageHistory"
|
|
class="chat-messages-area"
|
|
></message-history>
|
|
|
|
<!-- Chat Input - to the bottom -->
|
|
<chat-input
|
|
:current-message="currentMessage"
|
|
:is-loading="isLoading"
|
|
:max-length="2000"
|
|
:allow-file-upload="true"
|
|
:allow-voice-message="false"
|
|
:form-data="currentInputFormData"
|
|
@send-message="sendMessage"
|
|
@update-message="updateCurrentMessage"
|
|
@upload-file="handleFileUpload"
|
|
@record-voice="handleVoiceRecord"
|
|
@submit-form="submitFormFromInput"
|
|
ref="chatInput"
|
|
class="chat-input-area"
|
|
></chat-input>
|
|
|
|
</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);
|
|
}); |