191 lines
7.1 KiB
JavaScript
191 lines
7.1 KiB
JavaScript
export const ChatMessage = {
|
||
name: 'ChatMessage',
|
||
props: {
|
||
message: {
|
||
type: Object,
|
||
required: true,
|
||
validator: (message) => {
|
||
return message.id && message.content !== undefined && message.sender && message.type;
|
||
}
|
||
},
|
||
isSubmittingForm: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
apiPrefix: {
|
||
type: String,
|
||
default: ''
|
||
}
|
||
},
|
||
emits: ['image-loaded', 'retry-message', 'specialist-complete', 'specialist-error'],
|
||
data() {
|
||
return {
|
||
};
|
||
},
|
||
methods: {
|
||
handleSpecialistError(eventData) {
|
||
console.log('ChatMessage received specialist-error event:', eventData);
|
||
|
||
// Creëer een error message met correcte styling
|
||
this.message.type = 'error';
|
||
this.message.content = eventData.message || 'Er is een fout opgetreden bij het verwerken van uw verzoek.';
|
||
this.message.retryable = true;
|
||
this.message.error = true; // Voeg error flag toe voor styling
|
||
|
||
// Bubble up naar parent component voor verdere afhandeling
|
||
this.$emit('specialist-error', {
|
||
messageId: this.message.id,
|
||
...eventData
|
||
});
|
||
},
|
||
|
||
handleSpecialistComplete(eventData) {
|
||
console.log('ChatMessage received specialist-complete event:', eventData);
|
||
|
||
// Update de inhoud van het bericht met het antwoord
|
||
if (eventData.answer) {
|
||
console.log('Updating message content with answer:', eventData.answer);
|
||
this.message.content = eventData.answer;
|
||
} else {
|
||
console.error('No answer in specialist-complete event data');
|
||
}
|
||
|
||
// Bubble up naar parent component voor eventuele verdere afhandeling
|
||
this.$emit('specialist-complete', {
|
||
messageId: this.message.id,
|
||
answer: eventData.answer,
|
||
form_request: eventData.form_request, // Wordt nu door ChatApp verwerkt
|
||
result: eventData.result,
|
||
interactionId: eventData.interactionId,
|
||
taskId: eventData.taskId
|
||
});
|
||
},
|
||
|
||
formatMessage(content) {
|
||
if (!content) return '';
|
||
|
||
// Enhanced markdown-like formatting
|
||
return content
|
||
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
|
||
.replace(/\*(.*?)\*/g, '<em>$1</em>')
|
||
.replace(/`(.*?)`/g, '<code>$1</code>')
|
||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>')
|
||
.replace(/\n/g, '<br>');
|
||
},
|
||
|
||
|
||
removeMessage() {
|
||
// Dit zou een event moeten triggeren naar de parent component
|
||
},
|
||
|
||
reactToMessage(emoji) {
|
||
// Implementatie van reacties zou hier komen
|
||
},
|
||
|
||
getMessageClass() {
|
||
return `message ${this.message.sender}`;
|
||
}
|
||
},
|
||
template: `
|
||
<div :class="getMessageClass()" :data-message-id="message.id">
|
||
<!-- Normal text messages -->
|
||
<template v-if="message.type === 'text'">
|
||
<div class="message-content">
|
||
<!-- Voortgangstracker voor AI berichten met task_id - NU BINNEN DE BUBBLE -->
|
||
<progress-tracker
|
||
v-if="message.sender === 'ai' && message.taskId"
|
||
:task-id="message.taskId"
|
||
:api-prefix="apiPrefix"
|
||
class="message-progress"
|
||
@specialist-complete="handleSpecialistComplete"
|
||
@specialist-error="handleSpecialistError"
|
||
></progress-tracker>
|
||
|
||
<!-- View mode -->
|
||
<div>
|
||
<div
|
||
v-html="formatMessage(message.content)"
|
||
class="message-text"
|
||
></div>
|
||
<!-- Debug info -->
|
||
<div v-if="false" class="debug-info">
|
||
Content: {{ message.content ? message.content.length + ' chars' : 'empty' }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- Image messages -->
|
||
<template v-if="message.type === 'image'">
|
||
<div class="message-content">
|
||
<img
|
||
:src="message.imageUrl"
|
||
:alt="message.alt || 'Afbeelding'"
|
||
class="message-image"
|
||
@load="$emit('image-loaded')"
|
||
>
|
||
<div v-if="message.caption" class="image-caption">
|
||
{{ message.caption }}
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- File messages -->
|
||
<template v-if="message.type === 'file'">
|
||
<div class="message-content">
|
||
<div class="file-attachment">
|
||
<div class="file-icon">📎</div>
|
||
<div class="file-info">
|
||
<div class="file-name">{{ message.fileName }}</div>
|
||
<div class="file-size">{{ message.fileSize }}</div>
|
||
</div>
|
||
<a
|
||
:href="message.fileUrl"
|
||
download
|
||
class="file-download"
|
||
title="Download"
|
||
>
|
||
⬇️
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
|
||
<!-- System messages -->
|
||
<template v-if="message.type === 'system'">
|
||
<div class="system-message">
|
||
<span class="system-icon">ℹ️</span>
|
||
{{ message.content }}
|
||
</div>
|
||
</template>
|
||
|
||
<!-- Error messages -->
|
||
<template v-if="message.type === 'error'">
|
||
<div class="error-message">
|
||
<span class="error-icon">⚠️</span>
|
||
{{ message.content }}
|
||
<button
|
||
v-if="message.retryable"
|
||
@click="$emit('retry-message', message.id)"
|
||
class="retry-btn"
|
||
>
|
||
Probeer opnieuw
|
||
</button>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- Message reactions -->
|
||
<div v-if="message.reactions && message.reactions.length" class="message-reactions">
|
||
<span
|
||
v-for="reaction in message.reactions"
|
||
:key="reaction.emoji"
|
||
class="reaction"
|
||
@click="reactToMessage(reaction.emoji)"
|
||
>
|
||
{{ reaction.emoji }} {{ reaction.count }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
`
|
||
}; |