Fix CSAT responses not visible agent message bubble

- Fix csat form submission in widget
- Update vite config to allow cross origin requests
This commit is contained in:
Abhinav Raut
2026-02-26 00:35:49 +05:30
parent aeb153b15d
commit 45a80118cc
4 changed files with 61 additions and 37 deletions
@@ -1,25 +1,36 @@
<template>
<div v-if="csatResponse" class="mt-3 p-3 bg-muted/50 rounded-md border">
<div class="text-sm font-medium text-muted-foreground mb-2">
{{ $t('globals.terms.feedback') }}:
<div v-if="isCsatMessage" class="mt-3 pt-3 border-t border-border">
<!-- Submitted CSAT -->
<div v-if="isSubmitted && hasResponse">
<div class="text-xs text-muted-foreground mb-2">
{{ t('globals.terms.feedback', 1) }}
</div>
<div v-if="csatResponse.rating" class="flex items-center gap-2 mb-2">
<span class="text-lg">{{ getRatingEmoji(csatResponse.rating) }}</span>
<span class="text-sm font-medium">{{ getRatingText(csatResponse.rating) }}</span>
<span class="text-xs text-muted-foreground">{{ csatResponse.rating }}/5</span>
</div>
<p
v-if="csatResponse.feedback"
class="text-sm text-muted-foreground italic pl-3 border-l-2 border-muted"
>
{{ csatResponse.feedback }}
</p>
</div>
<!-- Rating -->
<div v-if="csatResponse.rating" class="flex items-center gap-2 mb-2">
<span class="text-lg">{{ getRatingEmoji(csatResponse.rating) }}</span>
<span class="text-sm font-medium">{{ getRatingText(csatResponse.rating) }}</span>
<span class="text-xs text-muted-foreground">({{ csatResponse.rating }}/5)</span>
</div>
<!-- Feedback -->
<div v-if="csatResponse.feedback" class="text-sm italic text-foreground">
"{{ csatResponse.feedback }}"
</div>
<span v-else-if="!isSubmitted" class="text-xs text-muted-foreground italic">
{{ t('globals.terms.awaitingResponse') }}
</span>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const props = defineProps({
message: {
@@ -28,37 +39,34 @@ const props = defineProps({
}
})
const csatResponse = computed(() => {
const meta = props.message.meta
if (!meta?.is_csat || !meta?.csat_submitted) {
return null
}
const isCsatMessage = computed(() => props.message.meta?.is_csat === true)
const isSubmitted = computed(() => props.message.meta?.csat_submitted === true)
const csatResponse = computed(() => {
if (!isSubmitted.value) return null
return {
rating: meta.submitted_rating || null,
feedback: meta.submitted_feedback || null
rating: props.message.meta.submitted_rating || null,
feedback: props.message.meta.submitted_feedback || null
}
})
const hasResponse = computed(() =>
csatResponse.value && (csatResponse.value.rating || csatResponse.value.feedback)
)
const getRatingEmoji = (rating) => {
const ratings = {
1: '😢',
2: '😕',
3: '😊',
4: '😃',
5: '🤩'
}
return ratings[rating] || ''
const emojis = { 1: '😢', 2: '😕', 3: '😊', 4: '😃', 5: '🤩' }
return emojis[rating] || ''
}
const getRatingText = (rating) => {
const ratings = {
1: 'Poor',
2: 'Fair',
3: 'Good',
4: 'Great',
5: 'Excellent'
const keys = {
1: 'globals.terms.poor',
2: 'globals.terms.fair',
3: 'globals.terms.good',
4: 'globals.terms.great',
5: 'globals.terms.excellent'
}
return ratings[rating] || ''
return keys[rating] ? t(keys[rating]) : ''
}
</script>
@@ -73,6 +73,9 @@
<!-- Attachments -->
<MessageAttachmentPreview :attachments="nonInlineAttachments" />
<!-- CSAT Response -->
<CSATResponseDisplay :message="message" />
<!-- Spinner for Pending Messages (outgoing only) -->
<Spinner v-if="isOutgoing && message.status === 'pending'" size="sm" />
@@ -129,6 +132,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@shared-ui/components/ui/av
import { Letter } from 'vue-letter'
import MessageAttachmentPreview from '@main/features/conversation/message/attachment/MessageAttachmentPreview.vue'
import MessageEnvelope from './MessageEnvelope.vue'
import CSATResponseDisplay from './CSATResponseDisplay.vue'
import api from '@main/api'
const props = defineProps({
@@ -165,6 +169,10 @@ const avatarFallback = computed(() => {
// Content sanitization - different processing for incoming vs outgoing
const sanitizedContent = computed(() => {
if (props.message.meta?.is_csat) {
return t('globals.messages.pleaseRateConversation')
}
let content = props.message.content || ''
if (isOutgoing.value) {
+7
View File
@@ -32,6 +32,7 @@ export default defineConfig(({ mode }) => {
// Separate cache per app to avoid stale/conflicting caches.
cacheDir: path.resolve(__dirname, `node_modules/.vite-${isWidget ? 'widget' : 'main'}`),
server: {
cors: { origin: "*" },
// Allow access to parent dir so shared-ui imports work in dev.
fs: {
allow: [path.resolve(__dirname)],
@@ -40,23 +41,29 @@ export default defineConfig(({ mode }) => {
proxy: {
'/api': {
target: 'http://127.0.0.1:9000',
changeOrigin: true,
},
'/widget.js': {
target: 'http://127.0.0.1:9000',
changeOrigin: true,
},
'/logout': {
target: 'http://127.0.0.1:9000',
changeOrigin: true,
},
'/uploads': {
target: 'http://127.0.0.1:9000',
changeOrigin: true,
},
'/ws': {
target: 'ws://127.0.0.1:9000',
ws: true,
changeOrigin: true,
},
'/widget/ws': {
target: 'ws://127.0.0.1:9000',
ws: true,
changeOrigin: true,
}
},
},
+2 -1
View File
@@ -1326,7 +1326,8 @@ func (m *Manager) SendCSATReply(actorUserID int, conversation models.Conversatio
message := fmt.Sprintf(csatReplyMessage, csatPublicURL)
// Store `is_csat` meta to identify and filter CSAT public url from the message.
meta := map[string]interface{}{
"is_csat": true,
"is_csat": true,
"csat_uuid": csat.UUID,
}
// Make recipient list.