feat: Add CSAT response submit action (#2506)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
parent
2f9637bde5
commit
dbddb1ece4
6 changed files with 103 additions and 29 deletions
|
@ -54,7 +54,7 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def message_update_params
|
def message_update_params
|
||||||
params.permit(message: [{ submitted_values: [:name, :title, :value] }])
|
params.permit(message: [{ submitted_values: [:name, :title, :value, { csat_survey_response: [:feedback_text, :rating] }] }])
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
def permitted_params
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="customer-satisfcation">
|
<div class="customer-satisfcation">
|
||||||
<div class="title">
|
<h6 class="title">
|
||||||
{{ $t('CSAT.TITLE') }}
|
{{ title }}
|
||||||
</div>
|
</h6>
|
||||||
<div class="ratings">
|
<div class="ratings">
|
||||||
<button
|
<button
|
||||||
v-for="rating in ratings"
|
v-for="rating in ratings"
|
||||||
:key="rating.key"
|
:key="rating.key"
|
||||||
class="emoji-button"
|
:class="buttonClass(rating)"
|
||||||
:class="{ selected: rating.key === selectedRating }"
|
|
||||||
@click="selectRating(rating)"
|
@click="selectRating(rating)"
|
||||||
>
|
>
|
||||||
{{ rating.emoji }}
|
{{ rating.emoji }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<form
|
<form
|
||||||
v-if="!hasSubmitted"
|
v-if="!isCSATSubmitted"
|
||||||
class="feedback-form"
|
class="feedback-form"
|
||||||
@submit.prevent="onSubmit()"
|
@submit.prevent="onSubmit()"
|
||||||
>
|
>
|
||||||
|
@ -27,7 +26,7 @@
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
class="button"
|
class="button"
|
||||||
:disabled="!selectedRating"
|
:disabled="isButtonDisabled"
|
||||||
:style="{ background: widgetColor, borderColor: widgetColor }"
|
:style="{ background: widgetColor, borderColor: widgetColor }"
|
||||||
>
|
>
|
||||||
<i v-if="!isUpdating" class="ion-ios-arrow-forward" />
|
<i v-if="!isUpdating" class="ion-ios-arrow-forward" />
|
||||||
|
@ -46,6 +45,12 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
Spinner,
|
Spinner,
|
||||||
},
|
},
|
||||||
|
props: {
|
||||||
|
messageContentAttributes: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
email: '',
|
email: '',
|
||||||
|
@ -53,19 +58,54 @@ export default {
|
||||||
selectedRating: null,
|
selectedRating: null,
|
||||||
isUpdating: false,
|
isUpdating: false,
|
||||||
feedback: '',
|
feedback: '',
|
||||||
hasSubmitted: false,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
widgetColor: 'appConfig/getWidgetColor',
|
widgetColor: 'appConfig/getWidgetColor',
|
||||||
}),
|
}),
|
||||||
|
isCSATSubmitted() {
|
||||||
|
return (
|
||||||
|
this.messageContentAttributes &&
|
||||||
|
this.messageContentAttributes.csat_survey_response
|
||||||
|
);
|
||||||
|
},
|
||||||
|
isButtonDisabled() {
|
||||||
|
return !(this.selectedRating && this.feedback);
|
||||||
|
},
|
||||||
|
title() {
|
||||||
|
return this.isCSATSubmitted
|
||||||
|
? this.$t('CSAT.SUBMITTED_TITLE')
|
||||||
|
: this.$t('CSAT.TITLE');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.isCSATSubmitted) {
|
||||||
|
const {
|
||||||
|
csat_survey_response: { rating },
|
||||||
|
} = this.messageContentAttributes;
|
||||||
|
this.selectedRating = rating;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onSubmit() {},
|
buttonClass(rating) {
|
||||||
|
return [
|
||||||
|
{ selected: rating.value === this.selectedRating },
|
||||||
|
{ disabled: this.isCSATSubmitted },
|
||||||
|
{ hover: this.isCSATSubmitted },
|
||||||
|
'emoji-button',
|
||||||
|
];
|
||||||
|
},
|
||||||
|
onSubmit() {
|
||||||
|
this.$emit('submit', {
|
||||||
|
rating: this.selectedRating,
|
||||||
|
feedback: this.feedback,
|
||||||
|
});
|
||||||
|
},
|
||||||
selectRating(rating) {
|
selectRating(rating) {
|
||||||
this.selectedRating = rating.key;
|
this.selectedRating = rating.value;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -77,54 +117,72 @@ export default {
|
||||||
|
|
||||||
.customer-satisfcation {
|
.customer-satisfcation {
|
||||||
@include light-shadow;
|
@include light-shadow;
|
||||||
|
|
||||||
background: $color-white;
|
background: $color-white;
|
||||||
border-bottom-left-radius: $space-smaller;
|
border-bottom-left-radius: $space-smaller;
|
||||||
color: $color-body;
|
border-radius: $space-small;
|
||||||
border-top: $space-micro solid $color-woot;
|
border-top: $space-micro solid $color-woot;
|
||||||
border-radius: $space-one;
|
color: $color-body;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
width: 75%;
|
margin-top: $space-smaller;
|
||||||
|
width: 80%;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: $font-size-default;
|
font-size: $font-size-default;
|
||||||
font-weight: $font-weight-medium;
|
font-weight: $font-weight-medium;
|
||||||
padding-top: $space-two;
|
padding: $space-two $space-one 0;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ratings {
|
.ratings {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
padding: $space-two $space-normal;
|
padding: $space-two $space-normal;
|
||||||
|
|
||||||
.emoji-button {
|
.emoji-button {
|
||||||
font-size: $font-size-big;
|
|
||||||
outline: none;
|
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
&.selected {
|
font-size: $font-size-big;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&.selected,
|
||||||
|
&:hover,
|
||||||
|
&:focus,
|
||||||
|
&:active {
|
||||||
filter: grayscale(0%);
|
filter: grayscale(0%);
|
||||||
transform: scale(1.32);
|
transform: scale(1.32);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
cursor: default;
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.feedback-form {
|
.feedback-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
input {
|
input {
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
padding: $space-one;
|
border-bottom-left-radius: $space-small;
|
||||||
|
border: 0;
|
||||||
border-top: 1px solid $color-border;
|
border-top: 1px solid $color-border;
|
||||||
|
padding: $space-one;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
appearance: none;
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-right-radius: $space-small;
|
||||||
font-size: $font-size-large;
|
font-size: $font-size-large;
|
||||||
height: auto;
|
height: auto;
|
||||||
margin-left: -1px;
|
margin-left: -1px;
|
||||||
appearance: none;
|
|
||||||
.spinner {
|
.spinner {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
@ -17,26 +17,26 @@ export const CSAT_RATINGS = [
|
||||||
{
|
{
|
||||||
key: 'disappointed',
|
key: 'disappointed',
|
||||||
emoji: '😞',
|
emoji: '😞',
|
||||||
value: 0,
|
value: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'expressionless',
|
key: 'expressionless',
|
||||||
emoji: '😑',
|
emoji: '😑',
|
||||||
value: 1,
|
value: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'neutral',
|
key: 'neutral',
|
||||||
emoji: '😐',
|
emoji: '😐',
|
||||||
value: 2,
|
value: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'grinning',
|
key: 'grinning',
|
||||||
emoji: '😀',
|
emoji: '😀',
|
||||||
value: 3,
|
value: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'smiling',
|
key: 'smiling',
|
||||||
emoji: '😍',
|
emoji: '😍',
|
||||||
value: 4,
|
value: 5,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -143,7 +143,7 @@ export default {
|
||||||
return (
|
return (
|
||||||
this.messageContentAttributes.submitted_email ||
|
this.messageContentAttributes.submitted_email ||
|
||||||
(this.messageContentAttributes.submitted_values &&
|
(this.messageContentAttributes.submitted_values &&
|
||||||
this.contentType !== 'form')
|
!['form', 'input_csat'].includes(this.contentType))
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
responseMessage() {
|
responseMessage() {
|
||||||
|
|
|
@ -44,7 +44,11 @@
|
||||||
<div v-if="isArticle">
|
<div v-if="isArticle">
|
||||||
<chat-article :items="messageContentAttributes.items"></chat-article>
|
<chat-article :items="messageContentAttributes.items"></chat-article>
|
||||||
</div>
|
</div>
|
||||||
<customer-satisfaction v-if="isCSAT" />
|
<customer-satisfaction
|
||||||
|
v-if="isCSAT"
|
||||||
|
:message-content-attributes="messageContentAttributes.submitted_values"
|
||||||
|
@submit="onCSATSubmit"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -121,6 +125,17 @@ export default {
|
||||||
messageId: this.messageId,
|
messageId: this.messageId,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onCSATSubmit({ feedback, rating }) {
|
||||||
|
this.onResponse({
|
||||||
|
submittedValues: {
|
||||||
|
csat_survey_response: {
|
||||||
|
rating,
|
||||||
|
feedback_text: feedback,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messageId: this.messageId,
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
},
|
},
|
||||||
"CSAT": {
|
"CSAT": {
|
||||||
"TITLE": "Rate your conversation",
|
"TITLE": "Rate your conversation",
|
||||||
|
"SUBMITTED_TITLE": "Thank you for submitting the rating",
|
||||||
"PLACEHOLDER": "Tell us more..."
|
"PLACEHOLDER": "Tell us more..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue