mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-11-04 04:57:51 +00:00 
			
		
		
		
	# Pull Request Template ## Description This PR introduces basic customization options for the CSAT survey: * **Display Type**: Option to use star ratings instead of emojis. * **Message Text**: Customize the survey message (up to 200 characters). * **Survey Rules**: Send surveys based on labels — trigger when a conversation has or doesn't have a specific label. Fixes https://linear.app/chatwoot/document/improve-csat-responses-a61cf30e054e ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? ### Loom videos **Website Channel (Widget)** https://www.loom.com/share/7f47836cde7940ae9d17b7997d060a18?sid=aad2ad0a-140a-4a09-8829-e01fa2e102c5 **Email Channel (Survey link)** https://www.loom.com/share/e92f4c4c0f73417ba300a25885e093ce?sid=4bb006f0-1c2a-4352-a232-8bf684e3d757 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules --------- Co-authored-by: Pranav <pranavrajs@gmail.com>
		
			
				
	
	
		
			153 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			Vue
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			153 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"
 | 
						|
      :display-type="messageContentAttributes.display_type"
 | 
						|
      :message="message"
 | 
						|
      :message-id="messageId"
 | 
						|
    />
 | 
						|
  </div>
 | 
						|
</template>
 |