Files
chatwoot/app/javascript/widget/components/AgentMessageBubble.vue
Sivin Varghese 3a693947b5 feat: Add RTL Support to Widget (#11022)
This PR adds RTL support to the web widget for improved right-to-left language compatibility, updates colors, and cleans up code.

Fixes https://linear.app/chatwoot/issue/CW-4089/rtl-issues-on-widget

https://github.com/chatwoot/chatwoot/issues/9791

Other PR: https://github.com/chatwoot/chatwoot/pull/11016
2025-03-21 09:39:03 -07:00

151 lines
4.1 KiB
Vue
Executable File

<script>
import { useMessageFormatter } from 'shared/composables/useMessageFormatter';
import ChatCard from 'shared/components/ChatCard.vue';
import ChatForm from 'shared/components/ChatForm.vue';
import ChatOptions from 'shared/components/ChatOptions.vue';
import ChatArticle from './template/Article.vue';
import EmailInput from './template/EmailInput.vue';
import CustomerSatisfaction from 'shared/components/CustomerSatisfaction.vue';
import IntegrationCard from './template/IntegrationCard.vue';
export default {
name: 'AgentMessageBubble',
components: {
ChatArticle,
ChatCard,
ChatForm,
ChatOptions,
EmailInput,
CustomerSatisfaction,
IntegrationCard,
},
props: {
message: { type: String, default: null },
contentType: { type: String, default: null },
messageType: { type: Number, default: null },
messageId: { type: Number, default: null },
messageContentAttributes: {
type: Object,
default: () => {},
},
},
setup() {
const { formatMessage, getPlainText, truncateMessage, highlightContent } =
useMessageFormatter();
return {
formatMessage,
getPlainText,
truncateMessage,
highlightContent,
};
},
computed: {
isTemplate() {
return this.messageType === 3;
},
isTemplateEmail() {
return this.contentType === 'input_email';
},
isCards() {
return this.contentType === 'cards';
},
isOptions() {
return this.contentType === 'input_select';
},
isForm() {
return this.contentType === 'form';
},
isArticle() {
return this.contentType === 'article';
},
isCSAT() {
return this.contentType === 'input_csat';
},
isIntegrations() {
return this.contentType === 'integrations';
},
},
methods: {
onResponse(messageResponse) {
this.$store.dispatch('message/update', messageResponse);
},
onOptionSelect(selectedOption) {
this.onResponse({
submittedValues: [selectedOption],
messageId: this.messageId,
});
},
onFormSubmit(formValues) {
const formValuesAsArray = Object.keys(formValues).map(key => ({
name: key,
value: formValues[key],
}));
this.onResponse({
submittedValues: formValuesAsArray,
messageId: this.messageId,
});
},
},
};
</script>
<template>
<div class="chat-bubble-wrap">
<div
v-if="
!isCards && !isOptions && !isForm && !isArticle && !isCards && !isCSAT
"
class="chat-bubble agent bg-n-background dark:bg-n-solid-3 text-n-slate-12"
>
<div
v-dompurify-html="formatMessage(message, false)"
class="message-content text-n-slate-12"
/>
<EmailInput
v-if="isTemplateEmail"
:message-id="messageId"
:message-content-attributes="messageContentAttributes"
/>
<IntegrationCard
v-if="isIntegrations"
:message-id="messageId"
:meeting-data="messageContentAttributes.data"
/>
</div>
<div v-if="isOptions">
<ChatOptions
:title="message"
:options="messageContentAttributes.items"
:hide-fields="!!messageContentAttributes.submitted_values"
@option-select="onOptionSelect"
/>
</div>
<ChatForm
v-if="isForm && !messageContentAttributes.submitted_values"
:items="messageContentAttributes.items"
:button-label="messageContentAttributes.button_label"
:submitted-values="messageContentAttributes.submitted_values"
@submit="onFormSubmit"
/>
<div v-if="isCards">
<ChatCard
v-for="item in messageContentAttributes.items"
:key="item.title"
:media-url="item.media_url"
:title="item.title"
:description="item.description"
:actions="item.actions"
/>
</div>
<div v-if="isArticle">
<ChatArticle :items="messageContentAttributes.items" />
</div>
<CustomerSatisfaction
v-if="isCSAT"
:message-content-attributes="messageContentAttributes.submitted_values"
:message-id="messageId"
/>
</div>
</template>