- Allowed_languages and default_language now part of Tenant Make iso Tenant - Introduction of Translation into Traicie Selection Specialist
406 lines
15 KiB
JavaScript
406 lines
15 KiB
JavaScript
// 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
|
|
|
|
// 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();
|
|
|
|
export const ChatInput = {
|
|
name: 'ChatInput',
|
|
components: {
|
|
'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]);
|
|
}
|
|
|
|
// Maak een benoemde handler voor betere cleanup
|
|
this.languageChangeHandler = (event) => {
|
|
if (event.detail && event.detail.language) {
|
|
this.handleLanguageChange(event);
|
|
}
|
|
};
|
|
|
|
// Luister naar taalwijzigingen
|
|
document.addEventListener('language-changed', this.languageChangeHandler);
|
|
},
|
|
beforeUnmount() {
|
|
// Verwijder event listener bij unmount met de benoemde handler
|
|
if (this.languageChangeHandler) {
|
|
document.removeEventListener('language-changed', this.languageChangeHandler);
|
|
}
|
|
},
|
|
props: {
|
|
currentMessage: {
|
|
type: String,
|
|
default: ''
|
|
},
|
|
isLoading: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
placeholder: {
|
|
type: String,
|
|
default: 'Typ je bericht hier... - Enter om te verzenden, Shift+Enter voor nieuwe regel'
|
|
},
|
|
maxLength: {
|
|
type: Number,
|
|
default: 2000
|
|
},
|
|
formData: {
|
|
type: Object,
|
|
default: null
|
|
},
|
|
},
|
|
emits: ['send-message', 'update-message', 'submit-form'],
|
|
watch: {
|
|
'formData.icon': {
|
|
handler(newIcon) {
|
|
if (newIcon && window.iconManager) {
|
|
window.iconManager.ensureIconsLoaded({}, [newIcon]);
|
|
}
|
|
},
|
|
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) {
|
|
this.localMessage = newVal;
|
|
},
|
|
localMessage(newVal) {
|
|
this.$emit('update-message', newVal);
|
|
this.autoResize();
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
localMessage: this.currentMessage,
|
|
formValues: {},
|
|
translatedPlaceholder: this.placeholder,
|
|
isTranslating: false,
|
|
languageChangeHandler: null
|
|
};
|
|
},
|
|
computed: {
|
|
characterCount() {
|
|
return this.localMessage.length;
|
|
},
|
|
|
|
isOverLimit() {
|
|
return this.characterCount > this.maxLength;
|
|
},
|
|
|
|
hasFormData() {
|
|
return this.formData && this.formData.fields &&
|
|
((Array.isArray(this.formData.fields) && this.formData.fields.length > 0) ||
|
|
(typeof this.formData.fields === 'object' && Object.keys(this.formData.fields).length > 0));
|
|
},
|
|
|
|
canSend() {
|
|
const hasValidForm = this.formData && this.validateForm();
|
|
const hasValidMessage = this.localMessage.trim() && !this.isOverLimit;
|
|
|
|
// We kunnen nu verzenden als er een geldig formulier OF een geldig bericht is
|
|
// Bij een formulier is aanvullende tekst optioneel
|
|
return (!this.isLoading) && (hasValidForm || hasValidMessage);
|
|
},
|
|
|
|
hasFormDataToSend() {
|
|
return this.formData && this.validateForm();
|
|
},
|
|
|
|
sendButtonText() {
|
|
if (this.isLoading) {
|
|
return 'Verzenden...';
|
|
}
|
|
return this.formData ? 'Verstuur formulier' : 'Verstuur bericht';
|
|
}
|
|
},
|
|
mounted() {
|
|
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: {
|
|
handleLanguageChange(event) {
|
|
if (event.detail && event.detail.language) {
|
|
this.translatePlaceholder(event.detail.language);
|
|
}
|
|
},
|
|
|
|
async translatePlaceholder(language) {
|
|
// Voorkom dubbele vertaling
|
|
if (this.isTranslating) {
|
|
console.log('Placeholder vertaling al bezig, overslaan...');
|
|
return;
|
|
}
|
|
|
|
// Zet de vertaling vlag
|
|
this.isTranslating = true;
|
|
|
|
// Gebruik de originele placeholder als basis voor vertaling
|
|
const originalText = this.placeholder;
|
|
|
|
try {
|
|
// Controleer of TranslationClient beschikbaar is
|
|
if (!window.TranslationClient || typeof window.TranslationClient.translate !== 'function') {
|
|
console.error('TranslationClient.translate is niet beschikbaar voor placeholder');
|
|
return;
|
|
}
|
|
|
|
// Gebruik TranslationClient zonder UI indicator
|
|
const apiPrefix = window.chatConfig?.apiPrefix || '';
|
|
const response = await window.TranslationClient.translate(
|
|
originalText,
|
|
language,
|
|
null, // source_lang (auto-detect)
|
|
'chat_input_placeholder', // context
|
|
apiPrefix // API prefix voor tenant routing
|
|
);
|
|
|
|
if (response.success) {
|
|
// Update de placeholder
|
|
this.translatedPlaceholder = response.translated_text;
|
|
} else {
|
|
console.error('Vertaling placeholder mislukt:', response.error);
|
|
}
|
|
} catch (error) {
|
|
console.error('Fout bij vertalen placeholder:', error);
|
|
} finally {
|
|
// Reset de vertaling vlag
|
|
this.isTranslating = false;
|
|
}
|
|
},
|
|
|
|
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) {
|
|
if (event.key === 'Enter' && !event.shiftKey) {
|
|
event.preventDefault();
|
|
this.sendMessage();
|
|
} else if (event.key === 'Escape') {
|
|
this.localMessage = '';
|
|
}
|
|
},
|
|
|
|
sendMessage() {
|
|
if (!this.canSend) return;
|
|
|
|
// Bij een formulier gaan we het formulier en optioneel bericht combineren
|
|
if (this.formData) {
|
|
// Valideer het formulier
|
|
if (this.validateForm()) {
|
|
// Verstuur het formulier, eventueel met aanvullende tekst
|
|
this.$emit('submit-form', this.formValues);
|
|
}
|
|
} else if (this.localMessage.trim()) {
|
|
// Verstuur normaal bericht zonder formulier
|
|
this.$emit('send-message');
|
|
}
|
|
},
|
|
|
|
getFormValuesForSending() {
|
|
// Geeft de huidige formulierwaarden terug voor verzending
|
|
return this.formValues;
|
|
},
|
|
|
|
// Reset het formulier en de waarden
|
|
resetForm() {
|
|
this.formValues = {};
|
|
this.initFormValues();
|
|
},
|
|
|
|
// Annuleer het formulier (wordt momenteel niet gebruikt)
|
|
cancelForm() {
|
|
this.formValues = {};
|
|
// We sturen geen emit meer, maar het kan nuttig zijn om in de toekomst te hebben
|
|
},
|
|
|
|
validateForm() {
|
|
if (!this.formData || !this.formData.fields) return false;
|
|
|
|
// Controleer of alle verplichte velden zijn ingevuld
|
|
let missingFields = [];
|
|
|
|
if (Array.isArray(this.formData.fields)) {
|
|
missingFields = this.formData.fields.filter(field => {
|
|
if (!field.required) return false;
|
|
const fieldId = field.id || field.name;
|
|
const value = this.formValues[fieldId];
|
|
return value === undefined || value === null || (typeof value === 'string' && !value.trim());
|
|
});
|
|
} else {
|
|
// Voor object-gebaseerde velden
|
|
Object.entries(this.formData.fields).forEach(([fieldId, field]) => {
|
|
if (field.required) {
|
|
const value = this.formValues[fieldId];
|
|
if (value === undefined || value === null || (typeof value === 'string' && !value.trim())) {
|
|
missingFields.push(field);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
return missingFields.length === 0;
|
|
},
|
|
|
|
autoResize() {
|
|
this.$nextTick(() => {
|
|
const textarea = this.$refs.messageInput;
|
|
if (textarea) {
|
|
textarea.style.height = 'auto';
|
|
textarea.style.height = Math.min(textarea.scrollHeight, 120) + 'px';
|
|
}
|
|
});
|
|
},
|
|
|
|
focusInput() {
|
|
this.$refs.messageInput?.focus();
|
|
},
|
|
|
|
clearInput() {
|
|
this.localMessage = '';
|
|
this.focusInput();
|
|
},
|
|
|
|
updateFormValues(newValues) {
|
|
// Controleer of er daadwerkelijk iets is veranderd om recursieve updates te voorkomen
|
|
if (JSON.stringify(newValues) !== JSON.stringify(this.formValues)) {
|
|
this.formValues = JSON.parse(JSON.stringify(newValues));
|
|
}
|
|
}
|
|
},
|
|
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>
|
|
<!-- 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 -->
|
|
<div v-if="!formData.fields" style="color: red; padding: 10px;">Fout: Geen velden gevonden in formulier</div>
|
|
<dynamic-form
|
|
v-if="formData && formData.fields"
|
|
:form-data="formData"
|
|
:form-values="formValues"
|
|
:is-submitting="isLoading"
|
|
:hide-actions="true"
|
|
@update:form-values="updateFormValues"
|
|
></dynamic-form>
|
|
|
|
<!-- Geen extra knoppen meer onder het formulier, alles gaat via de hoofdverzendknop -->
|
|
</div>
|
|
|
|
<div class="chat-input">
|
|
<!-- Main input area -->
|
|
<div class="input-main">
|
|
<textarea
|
|
ref="messageInput"
|
|
v-model="localMessage"
|
|
@keydown="handleKeydown"
|
|
:placeholder="translatedPlaceholder"
|
|
rows="1"
|
|
:disabled="isLoading"
|
|
:maxlength="maxLength"
|
|
class="message-input"
|
|
:class="{ 'over-limit': isOverLimit }"
|
|
></textarea>
|
|
|
|
<!-- Character counter -->
|
|
<div v-if="maxLength" class="character-counter" :class="{ 'over-limit': isOverLimit }">
|
|
{{ characterCount }}/{{ maxLength }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Input actions -->
|
|
<div class="input-actions">
|
|
|
|
<!-- Universele verzendknop (voor zowel berichten als formulieren) -->
|
|
<button
|
|
@click="sendMessage"
|
|
class="send-btn"
|
|
:class="{ 'form-mode': formData }"
|
|
:disabled="!canSend"
|
|
:title="formData ? 'Verstuur formulier' : 'Verstuur bericht'"
|
|
>
|
|
<span v-if="isLoading" class="loading-spinner">⏳</span>
|
|
<svg v-else width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
|
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`
|
|
}; |