504 lines
16 KiB
Vue
504 lines
16 KiB
Vue
<template>
|
|
<div class="chat-input-container">
|
|
<!-- Material Icons worden nu globaal geladen in scripts.html -->
|
|
<!-- Dynamisch formulier container -->
|
|
<div v-if="formData" class="dynamic-form-container">
|
|
<!-- De titel wordt in DynamicForm weergegeven en niet hier om dubbele titels te voorkomen -->
|
|
<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>
|
|
</template>
|
|
|
|
<script>
|
|
// Importeer de benodigde componenten
|
|
import DynamicForm from './DynamicForm.vue';
|
|
import { useIconManager } from '../js/composables/useIconManager.js';
|
|
import { useTranslationClient } from '../js/composables/useTranslation.js';
|
|
|
|
export default {
|
|
name: 'ChatInput',
|
|
components: {
|
|
'dynamic-form': DynamicForm
|
|
},
|
|
setup(props) {
|
|
const { watchIcon } = useIconManager();
|
|
const { translateSafe, isTranslating: isTranslatingText } = useTranslationClient();
|
|
|
|
// Watch formData.icon for automatic icon loading
|
|
watchIcon(() => props.formData?.icon);
|
|
|
|
return {
|
|
translateSafe,
|
|
isTranslatingText
|
|
};
|
|
},
|
|
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'],
|
|
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';
|
|
}
|
|
},
|
|
watch: {
|
|
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();
|
|
}
|
|
},
|
|
created() {
|
|
// Als er een formData.icon is, wordt deze automatisch geladen via useIconManager composable
|
|
// Geen expliciete window.iconManager calls meer nodig
|
|
|
|
// 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);
|
|
},
|
|
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));
|
|
}
|
|
},
|
|
beforeUnmount() {
|
|
// Verwijder event listener bij unmount met de benoemde handler
|
|
if (this.languageChangeHandler) {
|
|
document.removeEventListener('language-changed', this.languageChangeHandler);
|
|
}
|
|
},
|
|
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 {
|
|
// Gebruik moderne translateSafe composable
|
|
const apiPrefix = window.chatConfig?.apiPrefix || '';
|
|
const translatedText = await this.translateSafe(originalText, language, {
|
|
context: 'chat_input_placeholder',
|
|
apiPrefix,
|
|
fallbackText: originalText
|
|
});
|
|
|
|
// Update de placeholder
|
|
this.translatedPlaceholder = translatedText;
|
|
console.log('Placeholder succesvol vertaald naar:', language);
|
|
} catch (error) {
|
|
console.error('Fout bij vertalen placeholder:', error);
|
|
// Fallback naar originele tekst
|
|
this.translatedPlaceholder = originalText;
|
|
} 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));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* ChatInput component styling */
|
|
|
|
/* Algemene container */
|
|
.chat-input-container {
|
|
width: 100%;
|
|
padding: 10px;
|
|
background-color: var(--active-background-color);
|
|
color: var(--active-text-color);
|
|
border-top: 1px solid #e0e0e0;
|
|
font-family: Arial, sans-serif;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Input veld en knoppen */
|
|
.chat-input {
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 10px;
|
|
}
|
|
|
|
.input-main {
|
|
flex: 1;
|
|
position: relative;
|
|
/* Zorg ervoor dat er ruimte is voor de verzendknop */
|
|
margin-right: 0;
|
|
}
|
|
|
|
.message-input {
|
|
width: 100%;
|
|
min-height: 40px;
|
|
padding: 10px 60px 10px 15px; /* Meer rechter padding voor character counter */
|
|
border: 1px solid #ddd;
|
|
border-radius: 20px;
|
|
resize: none;
|
|
outline: none;
|
|
transition: border-color 0.2s;
|
|
font-family: Arial, sans-serif;
|
|
font-size: 14px;
|
|
/* Transparante achtergrond in plaats van wit */
|
|
background-color: transparent;
|
|
/* Box-sizing om padding correct te berekenen */
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* Character counter */
|
|
.character-counter {
|
|
position: absolute;
|
|
right: 15px;
|
|
bottom: 12px;
|
|
font-size: 12px;
|
|
color: #999;
|
|
pointer-events: none; /* Voorkom dat deze de textarea verstoort */
|
|
}
|
|
|
|
/* Character counter wordt rood bij overschrijding */
|
|
.character-counter.over-limit {
|
|
color: #ff4d4f;
|
|
font-weight: bold;
|
|
}
|
|
|
|
/* Input actions */
|
|
.input-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
flex-shrink: 0; /* Voorkom dat de knop krimpt */
|
|
}
|
|
|
|
/* Verzendknop */
|
|
.send-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 40px;
|
|
height: 40px;
|
|
background-color: var(--active-background-color);
|
|
color: var(--active-text-color);
|
|
border: 1px solid var(--active-text-color);
|
|
border-radius: 50%;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
flex-shrink: 0; /* Voorkom dat de knop krimpt */
|
|
}
|
|
|
|
.send-btn:hover {
|
|
background-color: var(--active-text-color);
|
|
color: var(--active-background-color);
|
|
}
|
|
|
|
.send-btn:disabled {
|
|
background-color: #ccc;
|
|
color: #666;
|
|
border-color: #ccc;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.send-btn.form-mode {
|
|
background-color: var(--active-background-color);
|
|
color: var(--active-text-color);
|
|
border-color: var(--active-text-color);
|
|
}
|
|
|
|
.send-btn.form-mode:hover {
|
|
background-color: var(--active-text-color);
|
|
color: var(--active-background-color);
|
|
}
|
|
|
|
/* Loading spinner */
|
|
.loading-spinner {
|
|
display: inline-block;
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
</style>
|