Eerste goed werkende versie van een formulier in de chat input.
This commit is contained in:
@@ -4,9 +4,19 @@ import { FormField } from '/static/assets/js/components/FormField.js';
|
|||||||
import { DynamicForm } from '/static/assets/js/components/DynamicForm.js';
|
import { DynamicForm } from '/static/assets/js/components/DynamicForm.js';
|
||||||
import { ChatMessage } from '/static/assets/js/components/ChatMessage.js';
|
import { ChatMessage } from '/static/assets/js/components/ChatMessage.js';
|
||||||
import { MessageHistory } from '/static/assets/js/components/MessageHistory.js';
|
import { MessageHistory } from '/static/assets/js/components/MessageHistory.js';
|
||||||
import { ChatInput } from '/static/assets/js/components/ChatInput.js';
|
|
||||||
import { ProgressTracker } from '/static/assets/js/components/ProgressTracker.js';
|
import { ProgressTracker } from '/static/assets/js/components/ProgressTracker.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
|
// Main Chat Application
|
||||||
export const ChatApp = {
|
export const ChatApp = {
|
||||||
name: 'ChatApp',
|
name: 'ChatApp',
|
||||||
@@ -175,9 +185,13 @@ export const ChatApp = {
|
|||||||
|
|
||||||
// Initialize form values if it's a form
|
// Initialize form values if it's a form
|
||||||
if (type === 'form' && formData) {
|
if (type === 'form' && formData) {
|
||||||
this.$set(this.formValues, message.id, {});
|
// Vue 3 compatibele manier om reactieve objecten bij te werken
|
||||||
|
this.formValues[message.id] = {};
|
||||||
formData.fields.forEach(field => {
|
formData.fields.forEach(field => {
|
||||||
this.$set(this.formValues[message.id], field.name, field.defaultValue || '');
|
const fieldName = field.name || field.id;
|
||||||
|
if (fieldName) {
|
||||||
|
this.formValues[message.id][fieldName] = field.defaultValue || '';
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,49 +270,6 @@ export const ChatApp = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Form handling
|
|
||||||
async submitForm(formData, messageId) {
|
|
||||||
this.isSubmittingForm = true;
|
|
||||||
|
|
||||||
console.log('Submitting form:', formData.title, this.formValues[messageId]);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await this.callAPI('/api/submit_form', {
|
|
||||||
formData: this.formValues[messageId],
|
|
||||||
formType: formData.title,
|
|
||||||
conversation_id: this.conversationId,
|
|
||||||
user_id: this.userId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
this.addMessage(
|
|
||||||
`✅ ${response.message || 'Formulier succesvol verzonden!'}`,
|
|
||||||
'ai',
|
|
||||||
'text'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove the form message
|
|
||||||
this.removeMessage(messageId);
|
|
||||||
} else {
|
|
||||||
this.addMessage(
|
|
||||||
`❌ Er ging iets mis: ${response.error || 'Onbekende fout'}`,
|
|
||||||
'ai',
|
|
||||||
'text'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
} 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'
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
this.isSubmittingForm = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async submitFormFromInput(formValues) {
|
async submitFormFromInput(formValues) {
|
||||||
this.isSubmittingForm = true;
|
this.isSubmittingForm = true;
|
||||||
|
|
||||||
@@ -324,7 +295,7 @@ export const ChatApp = {
|
|||||||
'text'
|
'text'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Wis het huidige formulier
|
// Wis het huidige formulier (ongeacht of het succesvol was of niet)
|
||||||
this.currentInputFormData = null;
|
this.currentInputFormData = null;
|
||||||
} else {
|
} else {
|
||||||
this.addMessage(
|
this.addMessage(
|
||||||
@@ -332,6 +303,8 @@ export const ChatApp = {
|
|||||||
'ai',
|
'ai',
|
||||||
'text'
|
'text'
|
||||||
);
|
);
|
||||||
|
// Wis ook hier het formulier na een fout
|
||||||
|
this.currentInputFormData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -341,6 +314,8 @@ export const ChatApp = {
|
|||||||
'ai',
|
'ai',
|
||||||
'text'
|
'text'
|
||||||
);
|
);
|
||||||
|
// Wis ook hier het formulier na een fout
|
||||||
|
this.currentInputFormData = null;
|
||||||
} finally {
|
} finally {
|
||||||
this.isSubmittingForm = false;
|
this.isSubmittingForm = false;
|
||||||
}
|
}
|
||||||
@@ -439,15 +414,23 @@ export const ChatApp = {
|
|||||||
handleSpecialistComplete(eventData) {
|
handleSpecialistComplete(eventData) {
|
||||||
console.log('ChatApp received specialist-complete:', eventData);
|
console.log('ChatApp received specialist-complete:', eventData);
|
||||||
|
|
||||||
// Als er een form_request is, voeg deze toe als nieuw bericht
|
// Als er een form_request is, toon deze in de ChatInput component
|
||||||
if (eventData.form_request) {
|
if (eventData.form_request) {
|
||||||
console.log('Adding form request as new message:', eventData.form_request);
|
console.log('Setting form request in ChatInput:', eventData.form_request);
|
||||||
|
|
||||||
// Converteer de form_request naar het verwachte formaat
|
try {
|
||||||
const formData = this.convertFormRequest(eventData.form_request);
|
// Converteer de form_request naar het verwachte formaat
|
||||||
|
const formData = this.convertFormRequest(eventData.form_request);
|
||||||
|
|
||||||
// Voeg het formulier toe als een nieuw AI bericht
|
// Stel het formulier in als currentInputFormData in plaats van als bericht toe te voegen
|
||||||
this.addMessage('', 'ai', 'form', formData);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -466,19 +449,44 @@ export const ChatApp = {
|
|||||||
convertFormRequest(formRequest) {
|
convertFormRequest(formRequest) {
|
||||||
console.log('Converting form request:', formRequest);
|
console.log('Converting form request:', formRequest);
|
||||||
|
|
||||||
// Converteer de fields van object naar array formaat
|
if (!formRequest) {
|
||||||
const fieldsArray = Object.entries(formRequest.fields || {}).map(([fieldId, fieldDef]) => ({
|
console.error('Geen geldig formRequest ontvangen');
|
||||||
id: fieldId,
|
return null;
|
||||||
name: fieldDef.name,
|
}
|
||||||
type: fieldDef.type,
|
|
||||||
description: fieldDef.description,
|
|
||||||
required: fieldDef.required || false,
|
|
||||||
defaultValue: fieldDef.default || '',
|
|
||||||
allowedValues: fieldDef.allowed_values || null
|
|
||||||
}));
|
|
||||||
|
|
||||||
return {
|
// Controleer of fields een object is voordat we converteren
|
||||||
title: formRequest.name,
|
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
|
||||||
|
}));
|
||||||
|
} 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
|
||||||
|
}));
|
||||||
|
} 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',
|
icon: formRequest.icon || 'form',
|
||||||
version: formRequest.version || '1.0',
|
version: formRequest.version || '1.0',
|
||||||
fields: fieldsArray
|
fields: fieldsArray
|
||||||
@@ -548,54 +556,6 @@ export const ChatApp = {
|
|||||||
this.$refs.searchInput?.focus();
|
this.$refs.searchInput?.focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSpecialistComplete(eventData) {
|
|
||||||
console.log('ChatApp received specialist-complete:', eventData);
|
|
||||||
|
|
||||||
// Als er een form_request is, stuur deze naar de ChatInput component
|
|
||||||
if (eventData.form_request) {
|
|
||||||
console.log('Providing form request to ChatInput:', eventData.form_request);
|
|
||||||
|
|
||||||
// Converteer de form_request naar het verwachte formaat
|
|
||||||
const formData = this.convertFormRequest(eventData.form_request);
|
|
||||||
|
|
||||||
// Update de currentInputFormData voor ChatInput
|
|
||||||
this.currentInputFormData = formData;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Converteer de fields van object naar array formaat
|
|
||||||
const fieldsArray = Object.entries(formRequest.fields || {}).map(([fieldId, fieldDef]) => ({
|
|
||||||
id: fieldId,
|
|
||||||
name: fieldDef.name,
|
|
||||||
type: fieldDef.type,
|
|
||||||
description: fieldDef.description,
|
|
||||||
required: fieldDef.required || false,
|
|
||||||
defaultValue: fieldDef.default || '',
|
|
||||||
allowedValues: fieldDef.allowed_values || null
|
|
||||||
}));
|
|
||||||
|
|
||||||
return {
|
|
||||||
title: formRequest.name,
|
|
||||||
icon: formRequest.icon || 'form',
|
|
||||||
version: formRequest.version || '1.0',
|
|
||||||
fields: fieldsArray
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
|
||||||
template: `
|
template: `
|
||||||
@@ -604,7 +564,6 @@ export const ChatApp = {
|
|||||||
<message-history
|
<message-history
|
||||||
:messages="displayMessages"
|
:messages="displayMessages"
|
||||||
:is-typing="isTyping"
|
:is-typing="isTyping"
|
||||||
:form-values="formValues"
|
|
||||||
:is-submitting-form="isSubmittingForm"
|
:is-submitting-form="isSubmittingForm"
|
||||||
:api-prefix="apiPrefix"
|
:api-prefix="apiPrefix"
|
||||||
:auto-scroll="true"
|
:auto-scroll="true"
|
||||||
@@ -641,16 +600,12 @@ export const ChatApp = {
|
|||||||
const initializeApp = () => {
|
const initializeApp = () => {
|
||||||
console.log('Initializing Chat Application');
|
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
|
// Get access to the existing Vue app instance
|
||||||
if (window.__vueApp) {
|
if (window.__vueApp) {
|
||||||
// Zorg ervoor dat alle componenten globaal beschikbaar zijn via window
|
|
||||||
window.TypingIndicator = TypingIndicator;
|
|
||||||
window.FormField = FormField;
|
|
||||||
window.DynamicForm = DynamicForm;
|
|
||||||
window.ChatMessage = ChatMessage;
|
|
||||||
window.MessageHistory = MessageHistory;
|
|
||||||
window.ChatInput = ChatInput;
|
|
||||||
window.ProgressTracker = ProgressTracker;
|
|
||||||
|
|
||||||
// Register ALL components globally
|
// Register ALL components globally
|
||||||
window.__vueApp.component('TypingIndicator', TypingIndicator);
|
window.__vueApp.component('TypingIndicator', TypingIndicator);
|
||||||
|
|||||||
@@ -1,9 +1,21 @@
|
|||||||
// static/js/components/ChatInput.js
|
// static/js/components/ChatInput.js
|
||||||
|
|
||||||
export const ChatInput = {
|
// 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
|
||||||
|
|
||||||
|
export const ChatInput = {
|
||||||
name: 'ChatInput',
|
name: 'ChatInput',
|
||||||
components: {
|
components: {
|
||||||
'dynamic-form': window.__vueApp ? DynamicForm : null
|
'dynamic-form': window.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]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
currentMessage: {
|
currentMessage: {
|
||||||
@@ -29,18 +41,44 @@ export const ChatInput = {
|
|||||||
},
|
},
|
||||||
emits: ['send-message', 'update-message', 'submit-form'],
|
emits: ['send-message', 'update-message', 'submit-form'],
|
||||||
watch: {
|
watch: {
|
||||||
formData: {
|
'formData.icon': {
|
||||||
handler(newFormData) {
|
handler(newIcon) {
|
||||||
console.log('ChatInput received formData:', newFormData);
|
if (newIcon && window.iconManager) {
|
||||||
if (newFormData) {
|
window.iconManager.ensureIconsLoaded({}, [newIcon]);
|
||||||
this.formValues = {}; // Reset formulierwaarden
|
|
||||||
this.showForm = true;
|
|
||||||
} else {
|
|
||||||
this.showForm = false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
immediate: true
|
immediate: true
|
||||||
},
|
},
|
||||||
|
formData: {
|
||||||
|
handler(newFormData, oldFormData) {
|
||||||
|
console.log('ChatInput formData changed:', newFormData);
|
||||||
|
|
||||||
|
if (!newFormData) {
|
||||||
|
console.log('FormData is null of undefined');
|
||||||
|
this.formValues = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controleer of velden aanwezig zijn
|
||||||
|
if (!newFormData.fields) {
|
||||||
|
console.error('FormData bevat geen velden!', newFormData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Velden in formData:', newFormData.fields);
|
||||||
|
console.log('Aantal velden:', Array.isArray(newFormData.fields)
|
||||||
|
? newFormData.fields.length
|
||||||
|
: Object.keys(newFormData.fields).length);
|
||||||
|
|
||||||
|
// Initialiseer formulierwaarden
|
||||||
|
this.initFormValues();
|
||||||
|
|
||||||
|
// Log de geïnitialiseerde waarden
|
||||||
|
console.log('Formulierwaarden geïnitialiseerd:', this.formValues);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
currentMessage(newVal) {
|
currentMessage(newVal) {
|
||||||
this.localMessage = newVal;
|
this.localMessage = newVal;
|
||||||
},
|
},
|
||||||
@@ -52,8 +90,7 @@ export const ChatInput = {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
localMessage: this.currentMessage,
|
localMessage: this.currentMessage,
|
||||||
formValues: {},
|
formValues: {}
|
||||||
showForm: false
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -72,20 +109,53 @@ export const ChatInput = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
canSend() {
|
canSend() {
|
||||||
const hasValidForm = this.showForm && this.formData && this.validateForm();
|
const hasValidForm = this.formData && this.validateForm();
|
||||||
const hasValidMessage = this.localMessage.trim() && !this.isOverLimit;
|
const hasValidMessage = this.localMessage.trim() && !this.isOverLimit;
|
||||||
|
|
||||||
return (!this.isLoading) && (hasValidForm || hasValidMessage);
|
return (!this.isLoading) && (hasValidForm || hasValidMessage);
|
||||||
},
|
},
|
||||||
|
|
||||||
sendButtonText() {
|
sendButtonText() {
|
||||||
return this.showForm ? 'Verstuur formulier' : 'Verstuur bericht';
|
if (this.isLoading) {
|
||||||
|
return 'Verzenden...';
|
||||||
|
}
|
||||||
|
return this.formData ? 'Verstuur formulier' : 'Verstuur bericht';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.autoResize();
|
this.autoResize();
|
||||||
|
// Debug informatie over formData bij initialisatie
|
||||||
|
console.log('ChatInput mounted, formData:', this.formData);
|
||||||
|
if (this.formData) {
|
||||||
|
console.log('FormData bij mount:', JSON.stringify(this.formData));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
initFormValues() {
|
||||||
|
if (this.formData && this.formData.fields) {
|
||||||
|
console.log('Initializing form values for fields:', this.formData.fields);
|
||||||
|
this.formValues = {};
|
||||||
|
|
||||||
|
// Verwerk array van velden
|
||||||
|
if (Array.isArray(this.formData.fields)) {
|
||||||
|
this.formData.fields.forEach(field => {
|
||||||
|
const fieldId = field.id || field.name;
|
||||||
|
if (fieldId) {
|
||||||
|
this.formValues[fieldId] = field.default !== undefined ? field.default : '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Verwerk object van velden
|
||||||
|
else if (typeof this.formData.fields === 'object') {
|
||||||
|
Object.entries(this.formData.fields).forEach(([fieldId, field]) => {
|
||||||
|
this.formValues[fieldId] = field.default !== undefined ? field.default : '';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Initialized form values:', this.formValues);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
handleKeydown(event) {
|
handleKeydown(event) {
|
||||||
if (event.key === 'Enter' && !event.shiftKey) {
|
if (event.key === 'Enter' && !event.shiftKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -98,14 +168,12 @@ export const ChatInput = {
|
|||||||
sendMessage() {
|
sendMessage() {
|
||||||
if (!this.canSend) return;
|
if (!this.canSend) return;
|
||||||
|
|
||||||
if (this.showForm && this.formData) {
|
if (this.formData) {
|
||||||
// Valideer het formulier
|
// Valideer het formulier
|
||||||
if (this.validateForm()) {
|
if (this.validateForm()) {
|
||||||
// Verstuur het formulier
|
// Verstuur het formulier
|
||||||
this.$emit('submit-form', this.formValues);
|
this.$emit('submit-form', this.formValues);
|
||||||
this.formValues = {};
|
this.formValues = {};
|
||||||
// Reset het formulier na verzenden
|
|
||||||
this.showForm = false;
|
|
||||||
}
|
}
|
||||||
} else if (this.localMessage.trim()) {
|
} else if (this.localMessage.trim()) {
|
||||||
// Verstuur normaal bericht
|
// Verstuur normaal bericht
|
||||||
@@ -113,6 +181,12 @@ export const ChatInput = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Deze methode houden we voor de volledigheid, maar zal niet meer direct worden aangeroepen
|
||||||
|
cancelForm() {
|
||||||
|
this.formValues = {};
|
||||||
|
// We sturen geen emit meer, maar het kan nuttig zijn om in de toekomst te hebben
|
||||||
|
},
|
||||||
|
|
||||||
validateForm() {
|
validateForm() {
|
||||||
if (!this.formData || !this.formData.fields) return false;
|
if (!this.formData || !this.formData.fields) return false;
|
||||||
|
|
||||||
@@ -160,42 +234,39 @@ export const ChatInput = {
|
|||||||
this.focusInput();
|
this.focusInput();
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleForm() {
|
|
||||||
this.showForm = !this.showForm;
|
|
||||||
if (!this.showForm) {
|
|
||||||
this.focusInput();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
submitForm() {
|
|
||||||
if (this.canSubmitForm) {
|
|
||||||
this.$emit('submit-form', { ...this.formValues });
|
|
||||||
this.showForm = false;
|
|
||||||
this.focusInput();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cancelForm() {
|
|
||||||
this.showForm = false;
|
|
||||||
this.focusInput();
|
|
||||||
},
|
|
||||||
|
|
||||||
updateFormValues(newValues) {
|
updateFormValues(newValues) {
|
||||||
this.formValues = { ...newValues };
|
this.formValues = { ...newValues };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<div class="chat-input-container">
|
<div class="chat-input-container">
|
||||||
<div v-if="formData && showForm" class="dynamic-form-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>
|
||||||
|
<!-- Dynamisch formulier container -->
|
||||||
|
<div v-if="formData" class="dynamic-form-container" style="margin-bottom: 10px; border: 1px solid #ddd; border-radius: 8px; padding: 15px 15px 5px 15px; position: relative; background-color: #f9f9f9; box-shadow: 0 2px 4px rgba(0,0,0,0.05);">
|
||||||
|
<!-- De titel wordt in DynamicForm weergegeven en niet hier om dubbele titels te voorkomen -->
|
||||||
|
<div v-if="!formData.fields" style="color: red; padding: 10px;">Fout: Geen velden gevonden in formulier</div>
|
||||||
<dynamic-form
|
<dynamic-form
|
||||||
|
v-if="formData && formData.fields"
|
||||||
:form-data="formData"
|
:form-data="formData"
|
||||||
:form-values="formValues"
|
:form-values="formValues"
|
||||||
:is-submitting="isLoading"
|
:is-submitting="isLoading"
|
||||||
:hide-actions="true"
|
:hide-actions="true"
|
||||||
@update:form-values="updateFormValues"
|
@update:form-values="updateFormValues"
|
||||||
></dynamic-form>
|
></dynamic-form>
|
||||||
|
|
||||||
|
<!-- Geen extra knoppen meer onder het formulier, alles gaat via de hoofdverzendknop -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes spin {
|
||||||
|
0% { transform: rotate(0deg); }
|
||||||
|
100% { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<div class="chat-input">
|
<div class="chat-input">
|
||||||
<!-- Main input area -->
|
<!-- Main input area -->
|
||||||
<div class="input-main">
|
<div class="input-main">
|
||||||
@@ -218,26 +289,15 @@ export const ChatInput = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Input actions -->
|
<!-- Input actions -->
|
||||||
<div class="input-actions">
|
<div class="input-actions">
|
||||||
<!-- Formulier toggle knop -->
|
|
||||||
<button
|
|
||||||
v-if="hasFormData"
|
|
||||||
@click="toggleForm"
|
|
||||||
class="form-toggle-btn"
|
|
||||||
:disabled="isLoading"
|
|
||||||
:class="{ 'active': showForm }"
|
|
||||||
:title="showForm ? 'Verberg formulier' : 'Toon formulier'"
|
|
||||||
>
|
|
||||||
<i class="material-icons">description</i>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Send button -->
|
<!-- Universele verzendknop (voor zowel berichten als formulieren) -->
|
||||||
<button
|
<button
|
||||||
@click="sendMessage"
|
@click="sendMessage"
|
||||||
class="send-btn"
|
class="send-btn"
|
||||||
:class="{ 'form-mode': showForm && formData }"
|
:class="{ 'form-mode': formData }"
|
||||||
:disabled="!canSend"
|
:disabled="!canSend"
|
||||||
:title="showForm ? 'Verstuur formulier' : 'Verstuur bericht'"
|
:title="formData ? 'Verstuur formulier' : 'Verstuur bericht'"
|
||||||
>
|
>
|
||||||
<span v-if="isLoading" class="loading-spinner">⏳</span>
|
<span v-if="isLoading" class="loading-spinner">⏳</span>
|
||||||
<svg v-else width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
<svg v-else width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
|||||||
@@ -8,10 +8,6 @@ export const ChatMessage = {
|
|||||||
return message.id && message.content !== undefined && message.sender && message.type;
|
return message.id && message.content !== undefined && message.sender && message.type;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
formValues: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
},
|
|
||||||
isSubmittingForm: {
|
isSubmittingForm: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
@@ -21,7 +17,7 @@ export const ChatMessage = {
|
|||||||
default: ''
|
default: ''
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
emits: ['submit-form', 'image-loaded', 'retry-message', 'specialist-complete', 'specialist-error'],
|
emits: ['image-loaded', 'retry-message', 'specialist-complete', 'specialist-error'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
};
|
};
|
||||||
@@ -57,7 +53,11 @@ export const ChatMessage = {
|
|||||||
// Bubble up naar parent component voor eventuele verdere afhandeling
|
// Bubble up naar parent component voor eventuele verdere afhandeling
|
||||||
this.$emit('specialist-complete', {
|
this.$emit('specialist-complete', {
|
||||||
messageId: this.message.id,
|
messageId: this.message.id,
|
||||||
...eventData
|
answer: eventData.answer,
|
||||||
|
form_request: eventData.form_request, // Wordt nu door ChatApp verwerkt
|
||||||
|
result: eventData.result,
|
||||||
|
interactionId: eventData.interactionId,
|
||||||
|
taskId: eventData.taskId
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -73,9 +73,6 @@ export const ChatMessage = {
|
|||||||
.replace(/\n/g, '<br>');
|
.replace(/\n/g, '<br>');
|
||||||
},
|
},
|
||||||
|
|
||||||
submitForm() {
|
|
||||||
this.$emit('submit-form', this.message.formData, this.message.id);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeMessage() {
|
removeMessage() {
|
||||||
// Dit zou een event moeten triggeren naar de parent component
|
// Dit zou een event moeten triggeren naar de parent component
|
||||||
@@ -86,9 +83,6 @@ export const ChatMessage = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getMessageClass() {
|
getMessageClass() {
|
||||||
if (this.message.type === 'form') {
|
|
||||||
return 'form-message';
|
|
||||||
}
|
|
||||||
return `message ${this.message.sender}`;
|
return `message ${this.message.sender}`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -157,16 +151,6 @@ export const ChatMessage = {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Dynamic forms -->
|
|
||||||
<template v-if="message.type === 'form'">
|
|
||||||
<dynamic-form
|
|
||||||
:form-data="message.formData"
|
|
||||||
:form-values="formValues[message.id] || {}"
|
|
||||||
:is-submitting="isSubmittingForm"
|
|
||||||
@submit="submitForm"
|
|
||||||
@cancel="removeMessage"
|
|
||||||
></dynamic-form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- System messages -->
|
<!-- System messages -->
|
||||||
<template v-if="message.type === 'system'">
|
<template v-if="message.type === 'system'">
|
||||||
|
|||||||
@@ -1,12 +1,42 @@
|
|||||||
export const DynamicForm = {
|
export const DynamicForm = {
|
||||||
name: 'DynamicForm',
|
name: 'DynamicForm',
|
||||||
|
created() {
|
||||||
|
// Zorg ervoor dat het icoon geladen wordt als iconManager beschikbaar is
|
||||||
|
if (window.iconManager && this.formData && this.formData.icon) {
|
||||||
|
window.iconManager.loadIcon(this.formData.icon);
|
||||||
|
}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
formData: {
|
formData: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
validator: (formData) => {
|
validator: (formData) => {
|
||||||
return formData && formData.title && formData.fields &&
|
// Controleer eerst of formData een geldig object is
|
||||||
(Array.isArray(formData.fields) || typeof formData.fields === 'object');
|
if (!formData || typeof formData !== 'object') {
|
||||||
|
console.error('FormData is niet een geldig object');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controleer of er een titel of naam is
|
||||||
|
if (!formData.title && !formData.name) {
|
||||||
|
console.error('FormData heeft geen title of name');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controleer of er velden zijn
|
||||||
|
if (!formData.fields) {
|
||||||
|
console.error('FormData heeft geen fields eigenschap');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Controleer of velden een array of object zijn
|
||||||
|
if (!Array.isArray(formData.fields) && typeof formData.fields !== 'object') {
|
||||||
|
console.error('FormData.fields is geen array of object');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('FormData is geldig:', formData);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
formValues: {
|
formValues: {
|
||||||
@@ -44,6 +74,14 @@ export const DynamicForm = {
|
|||||||
this.$emit('update:formValues', newValues);
|
this.$emit('update:formValues', newValues);
|
||||||
},
|
},
|
||||||
deep: true
|
deep: true
|
||||||
|
},
|
||||||
|
'formData.icon': {
|
||||||
|
handler(newIcon) {
|
||||||
|
if (newIcon && window.iconManager) {
|
||||||
|
window.iconManager.loadIcon(newIcon);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@@ -97,11 +135,14 @@ export const DynamicForm = {
|
|||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<div class="dynamic-form" :class="{ 'read-only': readOnly }">
|
<div class="dynamic-form" :class="{ 'read-only': readOnly }">
|
||||||
<div class="form-header" v-if="formData.title || formData.icon">
|
<div class="form-header" v-if="formData.title || formData.name || formData.icon" style="margin-bottom: 20px; display: flex; align-items: center;">
|
||||||
<div class="form-icon" v-if="formData.icon">
|
<div class="form-icon" v-if="formData.icon" style="margin-right: 10px; display: flex; align-items: center;">
|
||||||
<i class="material-icons">{{ formData.icon }}</i>
|
<span class="material-symbols-outlined" style="font-size: 24px; color: #4285f4;">{{ formData.icon }}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="form-title" style="font-weight: bold; font-size: 1.2em; color: #333;">{{ formData.title || formData.name }}</div>
|
||||||
|
<div v-if="formData.description" class="form-description" style="margin-top: 5px; color: #666; font-size: 0.9em;">{{ formData.description }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-title">{{ formData.title }}</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="readOnly" class="form-readonly">
|
<div v-if="readOnly" class="form-readonly">
|
||||||
@@ -148,7 +189,7 @@ export const DynamicForm = {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form v-else @submit.prevent="handleSubmit" novalidate>
|
<form v-else @submit.prevent="handleSubmit" novalidate>
|
||||||
<div class="form-fields">
|
<div class="form-fields" style="margin-top: 10px;">
|
||||||
<template v-if="Array.isArray(formData.fields)">
|
<template v-if="Array.isArray(formData.fields)">
|
||||||
<form-field
|
<form-field
|
||||||
v-for="field in formData.fields"
|
v-for="field in formData.fields"
|
||||||
|
|||||||
@@ -63,84 +63,109 @@ export const FormField = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<div class="form-field">
|
<div class="form-field" style="margin-bottom: 15px; display: grid; grid-template-columns: 35% 65%; align-items: center;">
|
||||||
<label :for="fieldId">
|
<!-- Label voor alle velden behalve boolean/checkbox die een speciale behandeling krijgen -->
|
||||||
{{ field.name }}
|
<label v-if="fieldType !== 'checkbox'" :for="fieldId" style="margin-right: 10px; font-weight: 500;">
|
||||||
<span v-if="field.required" class="required">*</span>
|
{{ field.name }}
|
||||||
|
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- Tekstinvoer (string/str) -->
|
<!-- Container voor input velden -->
|
||||||
<input
|
<div style="width: 100%;">
|
||||||
v-if="fieldType === 'text'"
|
<!-- Tekstinvoer (string/str) -->
|
||||||
:id="fieldId"
|
<input
|
||||||
type="text"
|
v-if="fieldType === 'text'"
|
||||||
v-model="value"
|
:id="fieldId"
|
||||||
:required="field.required"
|
type="text"
|
||||||
:placeholder="field.placeholder || ''"
|
v-model="value"
|
||||||
:title="description"
|
:required="field.required"
|
||||||
>
|
:placeholder="field.placeholder || ''"
|
||||||
|
:title="description"
|
||||||
<!-- Numerieke invoer (int/float) -->
|
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||||
<input
|
|
||||||
v-if="fieldType === 'number'"
|
|
||||||
:id="fieldId"
|
|
||||||
type="number"
|
|
||||||
v-model.number="value"
|
|
||||||
:required="field.required"
|
|
||||||
:step="stepValue"
|
|
||||||
:placeholder="field.placeholder || ''"
|
|
||||||
:title="description"
|
|
||||||
>
|
|
||||||
|
|
||||||
<!-- Tekstvlak (text) -->
|
|
||||||
<textarea
|
|
||||||
v-if="fieldType === 'textarea'"
|
|
||||||
:id="fieldId"
|
|
||||||
v-model="value"
|
|
||||||
:required="field.required"
|
|
||||||
:rows="field.rows || 3"
|
|
||||||
:placeholder="field.placeholder || ''"
|
|
||||||
:title="description"
|
|
||||||
></textarea>
|
|
||||||
|
|
||||||
<!-- Dropdown (enum) -->
|
|
||||||
<select
|
|
||||||
v-if="fieldType === 'select'"
|
|
||||||
:id="fieldId"
|
|
||||||
v-model="value"
|
|
||||||
:required="field.required"
|
|
||||||
:title="description"
|
|
||||||
>
|
|
||||||
<option v-if="!field.required" value="">Selecteer een optie</option>
|
|
||||||
<option
|
|
||||||
v-for="option in (field.allowedValues || field.allowed_values || [])"
|
|
||||||
:key="option"
|
|
||||||
:value="option"
|
|
||||||
>
|
>
|
||||||
{{ option }}
|
|
||||||
</option>
|
<!-- Numerieke invoer (int/float) -->
|
||||||
</select>
|
<input
|
||||||
<!-- Debug info voor select field -->
|
v-if="fieldType === 'number'"
|
||||||
<div v-if="fieldType === 'select' && (!(field.allowedValues || field.allowed_values) || (field.allowedValues || field.allowed_values).length === 0)" class="field-error">
|
:id="fieldId"
|
||||||
Geen opties beschikbaar voor dit veld.
|
type="number"
|
||||||
<pre style="font-size: 10px; color: #999;">{{ JSON.stringify(field, null, 2) }}</pre>
|
v-model.number="value"
|
||||||
|
:required="field.required"
|
||||||
|
:step="stepValue"
|
||||||
|
:placeholder="field.placeholder || ''"
|
||||||
|
:title="description"
|
||||||
|
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- Tekstvlak (text) -->
|
||||||
|
<textarea
|
||||||
|
v-if="fieldType === 'textarea'"
|
||||||
|
:id="fieldId"
|
||||||
|
v-model="value"
|
||||||
|
:required="field.required"
|
||||||
|
:rows="field.rows || 3"
|
||||||
|
:placeholder="field.placeholder || ''"
|
||||||
|
:title="description"
|
||||||
|
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; resize: vertical; box-sizing: border-box;"
|
||||||
|
></textarea>
|
||||||
|
|
||||||
|
<!-- Dropdown (enum) -->
|
||||||
|
<select
|
||||||
|
v-if="fieldType === 'select'"
|
||||||
|
:id="fieldId"
|
||||||
|
v-model="value"
|
||||||
|
:required="field.required"
|
||||||
|
:title="description"
|
||||||
|
style="width: 100%; padding: 8px; border-radius: 4px; border: 1px solid #ddd; background-color: white; box-sizing: border-box;"
|
||||||
|
>
|
||||||
|
<option v-if="!field.required" value="">Selecteer een optie</option>
|
||||||
|
<option
|
||||||
|
v-for="option in (field.allowedValues || field.allowed_values || [])"
|
||||||
|
:key="option"
|
||||||
|
:value="option"
|
||||||
|
>
|
||||||
|
{{ option }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<!-- Debug info voor select field -->
|
||||||
|
<div v-if="fieldType === 'select' && (!(field.allowedValues || field.allowed_values) || (field.allowedValues || field.allowed_values).length === 0)"
|
||||||
|
style="color: #d93025; font-size: 0.85em; margin-top: 4px;">
|
||||||
|
Geen opties beschikbaar voor dit veld.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Checkbox (boolean) -->
|
<!-- Toggle switch voor boolean velden, met speciale layout voor deze velden -->
|
||||||
<div v-if="fieldType === 'checkbox'" class="checkbox-container">
|
<div v-if="fieldType === 'checkbox'" style="grid-column: 1 / span 2; display: flex; align-items: center;">
|
||||||
<label class="checkbox-label">
|
<div class="toggle-switch" style="position: relative; display: inline-block; width: 50px; height: 24px;">
|
||||||
<input
|
<input
|
||||||
:id="fieldId"
|
:id="fieldId"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
:required="field.required"
|
:required="field.required"
|
||||||
:title="description"
|
:title="description"
|
||||||
|
style="opacity: 0; width: 0; height: 0;"
|
||||||
>
|
>
|
||||||
<span class="checkbox-text">{{ field.description || 'Ja' }}</span>
|
<span
|
||||||
|
class="toggle-slider"
|
||||||
|
style="position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: .4s; border-radius: 24px;"
|
||||||
|
:style="{ backgroundColor: value ? '#4CAF50' : '#ccc' }"
|
||||||
|
@click="value = !value"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="toggle-knob"
|
||||||
|
style="position: absolute; content: ''; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: .4s; border-radius: 50%;"
|
||||||
|
:style="{ transform: value ? 'translateX(26px)' : 'translateX(0)' }"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<label :for="fieldId" class="checkbox-label" style="margin-left: 10px; cursor: pointer;">
|
||||||
|
{{ field.name }}
|
||||||
|
<span v-if="field.required" class="required" style="color: #d93025; margin-left: 2px;">*</span>
|
||||||
|
<span class="checkbox-description" style="display: block; font-size: 0.85em; color: #666;">
|
||||||
|
{{ field.description || '' }}
|
||||||
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Geen beschrijving meer tonen, alleen als tooltip die al gedefinieerd is in de inputs -->
|
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// static/js/components/MaterialIconManager.js
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Een hulpklasse om Material Symbols Outlined iconen te beheren
|
||||||
|
* en dynamisch toe te voegen aan de pagina indien nodig.
|
||||||
|
*/
|
||||||
|
export const MaterialIconManager = {
|
||||||
|
name: 'MaterialIconManager',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loadedIconSets: [],
|
||||||
|
defaultOptions: {
|
||||||
|
opsz: 24, // Optimale grootte: 24px
|
||||||
|
wght: 400, // Gewicht: normaal
|
||||||
|
FILL: 0, // Vulling: niet gevuld
|
||||||
|
GRAD: 0 // Kleurverloop: geen
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* Zorgt ervoor dat de Material Symbols Outlined stijlbladen zijn geladen
|
||||||
|
* @param {Object} options - Opties voor het icoon (opsz, wght, FILL, GRAD)
|
||||||
|
* @param {Array} iconNames - Optionele lijst met specifieke iconen om te laden
|
||||||
|
*/
|
||||||
|
ensureIconsLoaded(options = {}, iconNames = []) {
|
||||||
|
const opts = { ...this.defaultOptions, ...options };
|
||||||
|
const styleUrl = this.buildStyleUrl(opts, iconNames);
|
||||||
|
|
||||||
|
// Controleer of deze specifieke set al is geladen
|
||||||
|
if (!this.loadedIconSets.includes(styleUrl)) {
|
||||||
|
this.loadStylesheet(styleUrl);
|
||||||
|
this.loadedIconSets.push(styleUrl);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bouwt de URL voor het stijlblad
|
||||||
|
*/
|
||||||
|
buildStyleUrl(options, iconNames = []) {
|
||||||
|
let url = `https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@${options.opsz},${options.wght},${options.FILL},${options.GRAD}`;
|
||||||
|
|
||||||
|
// Voeg specifieke iconNames toe als deze zijn opgegeven
|
||||||
|
if (iconNames.length > 0) {
|
||||||
|
url += `&icon_names=${iconNames.join(',')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Laadt een stijlblad dynamisch
|
||||||
|
*/
|
||||||
|
loadStylesheet(url) {
|
||||||
|
const link = document.createElement('link');
|
||||||
|
link.rel = 'stylesheet';
|
||||||
|
link.href = url;
|
||||||
|
document.head.appendChild(link);
|
||||||
|
console.log(`Material Symbols Outlined geladen: ${url}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Singleton instantie om te gebruiken in de hele applicatie
|
||||||
|
export const iconManager = new Vue(MaterialIconManager);
|
||||||
@@ -10,10 +10,6 @@ export const MessageHistory = {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
formValues: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
},
|
|
||||||
isSubmittingForm: {
|
isSubmittingForm: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
@@ -76,9 +72,6 @@ export const MessageHistory = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
handleSubmitForm(formData, messageId) {
|
|
||||||
this.$emit('submit-form', formData, messageId);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleImageLoaded() {
|
handleImageLoaded() {
|
||||||
// Auto-scroll when images load to maintain position
|
// Auto-scroll when images load to maintain position
|
||||||
@@ -129,10 +122,8 @@ export const MessageHistory = {
|
|||||||
<!-- The actual message -->
|
<!-- The actual message -->
|
||||||
<chat-message
|
<chat-message
|
||||||
:message="message"
|
:message="message"
|
||||||
:form-values="formValues"
|
|
||||||
:is-submitting-form="isSubmittingForm"
|
:is-submitting-form="isSubmittingForm"
|
||||||
:api-prefix="apiPrefix"
|
:api-prefix="apiPrefix"
|
||||||
@submit-form="handleSubmitForm"
|
|
||||||
@image-loaded="handleImageLoaded"
|
@image-loaded="handleImageLoaded"
|
||||||
@specialist-complete="$emit('specialist-complete', $event)"
|
@specialist-complete="$emit('specialist-complete', $event)"
|
||||||
@specialist-error="$emit('specialist-error', $event)"
|
@specialist-error="$emit('specialist-error', $event)"
|
||||||
@@ -143,7 +134,6 @@ export const MessageHistory = {
|
|||||||
<!-- Typing indicator -->
|
<!-- Typing indicator -->
|
||||||
<typing-indicator v-if="isTyping"></typing-indicator>
|
<typing-indicator v-if="isTyping"></typing-indicator>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
@@ -189,24 +189,25 @@ export const ProgressTracker = {
|
|||||||
console.log('Specialist Complete Data:', data);
|
console.log('Specialist Complete Data:', data);
|
||||||
|
|
||||||
// Extract answer from data.result.answer
|
// Extract answer from data.result.answer
|
||||||
if (data.result && data.result.answer) {
|
if (data.result) {
|
||||||
this.finalAnswer = data.result.answer;
|
if (data.result.answer) {
|
||||||
|
this.finalAnswer = data.result.answer;
|
||||||
|
console.log('Final Answer:', this.finalAnswer);
|
||||||
|
|
||||||
console.log('Final Answer:', this.finalAnswer);
|
// Direct update van de parent message als noodoplossing
|
||||||
|
try {
|
||||||
// Direct update van de parent message als noodoplossing
|
if (this.$parent && this.$parent.message) {
|
||||||
try {
|
console.log('Direct update parent message');
|
||||||
if (this.$parent && this.$parent.message) {
|
this.$parent.message.content = data.result.answer;
|
||||||
console.log('Direct update parent message');
|
}
|
||||||
this.$parent.message.content = data.result.answer;
|
} catch(err) {
|
||||||
|
console.error('Error updating parent message:', err);
|
||||||
}
|
}
|
||||||
} catch(err) {
|
|
||||||
console.error('Error updating parent message:', err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit event to parent met alle relevante data inclusief form_request
|
// Emit event to parent met alle relevante data inclusief form_request
|
||||||
this.$emit('specialist-complete', {
|
this.$emit('specialist-complete', {
|
||||||
answer: data.result.answer,
|
answer: data.result.answer || '',
|
||||||
form_request: data.result.form_request, // Voeg form_request toe
|
form_request: data.result.form_request, // Voeg form_request toe
|
||||||
result: data.result,
|
result: data.result,
|
||||||
interactionId: data.interaction_id,
|
interactionId: data.interaction_id,
|
||||||
|
|||||||
135
eveai_chat_client/static/js/iconManager.js
Normal file
135
eveai_chat_client/static/js/iconManager.js
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
// static/js/iconManager.js
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Een eenvoudige standalone icon manager voor Material Symbols Outlined
|
||||||
|
* Deze kan direct worden gebruikt zonder Vue
|
||||||
|
*/
|
||||||
|
window.iconManager = {
|
||||||
|
loadedIcons: [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Laadt een Material Symbols Outlined icoon als het nog niet is geladen
|
||||||
|
* @param {string} iconName - Naam van het icoon
|
||||||
|
* @param {Object} options - Opties voor het icoon (opsz, wght, FILL, GRAD)
|
||||||
|
*/
|
||||||
|
loadIcon: function(iconName, options = {}) {
|
||||||
|
if (!iconName) return;
|
||||||
|
|
||||||
|
if (this.loadedIcons.includes(iconName)) {
|
||||||
|
return; // Icoon is al geladen
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
opsz: 24,
|
||||||
|
wght: 400,
|
||||||
|
FILL: 0,
|
||||||
|
GRAD: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const opts = { ...defaultOptions, ...options };
|
||||||
|
|
||||||
|
// Genereer unieke ID voor het stylesheet element
|
||||||
|
const styleId = `material-symbols-${iconName}`;
|
||||||
|
|
||||||
|
// Controleer of het stylesheet al bestaat
|
||||||
|
if (!document.getElementById(styleId)) {
|
||||||
|
const link = document.createElement('link');
|
||||||
|
link.id = styleId;
|
||||||
|
link.rel = 'stylesheet';
|
||||||
|
link.href = `https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@${opts.opsz},${opts.wght},${opts.FILL},${opts.GRAD}&icon_names=${iconName}`;
|
||||||
|
document.head.appendChild(link);
|
||||||
|
console.log(`Material Symbol geladen: ${iconName}`);
|
||||||
|
|
||||||
|
this.loadedIcons.push(iconName);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Laadt een set van Material Symbols Outlined iconen
|
||||||
|
* @param {Array} iconNames - Array met icoonnamen
|
||||||
|
* @param {Object} options - Opties voor de iconen
|
||||||
|
*/
|
||||||
|
loadIcons: function(iconNames, options = {}) {
|
||||||
|
if (!iconNames || !Array.isArray(iconNames) || iconNames.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter alleen iconen die nog niet zijn geladen
|
||||||
|
const newIcons = iconNames.filter(icon => !this.loadedIcons.includes(icon));
|
||||||
|
|
||||||
|
if (newIcons.length === 0) {
|
||||||
|
return; // Alle iconen zijn al geladen
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
opsz: 24,
|
||||||
|
wght: 400,
|
||||||
|
FILL: 0,
|
||||||
|
GRAD: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const opts = { ...defaultOptions, ...options };
|
||||||
|
|
||||||
|
// Genereer unieke ID voor het stylesheet element
|
||||||
|
const styleId = `material-symbols-set-${newIcons.join('-')}`;
|
||||||
|
|
||||||
|
// Controleer of het stylesheet al bestaat
|
||||||
|
if (!document.getElementById(styleId)) {
|
||||||
|
const link = document.createElement('link');
|
||||||
|
link.id = styleId;
|
||||||
|
link.rel = 'stylesheet';
|
||||||
|
link.href = `https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@${opts.opsz},${opts.wght},${opts.FILL},${opts.GRAD}&icon_names=${newIcons.join(',')}`;
|
||||||
|
document.head.appendChild(link);
|
||||||
|
console.log(`Material Symbols geladen: ${newIcons.join(', ')}`);
|
||||||
|
|
||||||
|
// Voeg de nieuwe iconen toe aan de geladen lijst
|
||||||
|
this.loadedIcons.push(...newIcons);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Functie om iconManager toe te voegen aan het DynamicForm component
|
||||||
|
function initDynamicFormWithIcons() {
|
||||||
|
if (window.DynamicForm) {
|
||||||
|
const originalCreated = window.DynamicForm.created || function() {};
|
||||||
|
|
||||||
|
window.DynamicForm.created = function() {
|
||||||
|
// Roep de oorspronkelijke created methode aan als die bestond
|
||||||
|
originalCreated.call(this);
|
||||||
|
|
||||||
|
// 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 = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
window.DynamicForm.watch['formData.icon'] = {
|
||||||
|
handler: function(newIcon) {
|
||||||
|
if (newIcon) {
|
||||||
|
window.iconManager.loadIcon(newIcon);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('DynamicForm is uitgebreid met iconManager functionaliteit');
|
||||||
|
} else {
|
||||||
|
console.warn('DynamicForm component is niet beschikbaar. iconManager kan niet worden toegevoegd.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user