downvote with reason (#24922)

This commit is contained in:
znn
2025-09-02 07:27:04 +05:30
committed by GitHub
parent 84d09b8b8a
commit dd6547de06
6 changed files with 59 additions and 6 deletions

View File

@@ -508,7 +508,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
}, [mutateAppConversationData, handleConversationIdInfoChange]) }, [mutateAppConversationData, handleConversationIdInfoChange])
const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => { const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => {
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, appId) await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating, content: feedback.content } }, isInstalledApp, appId)
notify({ type: 'success', message: t('common.api.success') }) notify({ type: 'success', message: t('common.api.success') })
}, [isInstalledApp, appId, t, notify]) }, [isInstalledApp, appId, t, notify])

View File

@@ -20,6 +20,8 @@ import EditReplyModal from '@/app/components/app/annotation/edit-annotation-moda
import Log from '@/app/components/base/chat/chat/log' import Log from '@/app/components/base/chat/chat/log'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button' import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import NewAudioButton from '@/app/components/base/new-audio-button' import NewAudioButton from '@/app/components/base/new-audio-button'
import Modal from '@/app/components/base/modal/modal'
import Textarea from '@/app/components/base/textarea'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
type OperationProps = { type OperationProps = {
@@ -32,6 +34,7 @@ type OperationProps = {
hasWorkflowProcess: boolean hasWorkflowProcess: boolean
noChatInput?: boolean noChatInput?: boolean
} }
const Operation: FC<OperationProps> = ({ const Operation: FC<OperationProps> = ({
item, item,
question, question,
@@ -52,6 +55,8 @@ const Operation: FC<OperationProps> = ({
onRegenerate, onRegenerate,
} = useChatContext() } = useChatContext()
const [isShowReplyModal, setIsShowReplyModal] = useState(false) const [isShowReplyModal, setIsShowReplyModal] = useState(false)
const [isShowFeedbackModal, setIsShowFeedbackModal] = useState(false)
const [feedbackContent, setFeedbackContent] = useState('')
const { const {
id, id,
isOpeningStatement, isOpeningStatement,
@@ -70,14 +75,29 @@ const Operation: FC<OperationProps> = ({
return messageContent return messageContent
}, [agent_thoughts, messageContent]) }, [agent_thoughts, messageContent])
const handleFeedback = async (rating: 'like' | 'dislike' | null) => { const handleFeedback = async (rating: 'like' | 'dislike' | null, content?: string) => {
if (!config?.supportFeedback || !onFeedback) if (!config?.supportFeedback || !onFeedback)
return return
await onFeedback?.(id, { rating }) await onFeedback?.(id, { rating, content })
setLocalFeedback({ rating }) setLocalFeedback({ rating })
} }
const handleThumbsDown = () => {
setIsShowFeedbackModal(true)
}
const handleFeedbackSubmit = async () => {
await handleFeedback('dislike', feedbackContent)
setFeedbackContent('')
setIsShowFeedbackModal(false)
}
const handleFeedbackCancel = () => {
setFeedbackContent('')
setIsShowFeedbackModal(false)
}
const operationWidth = useMemo(() => { const operationWidth = useMemo(() => {
let width = 0 let width = 0
if (!isOpeningStatement) if (!isOpeningStatement)
@@ -153,7 +173,7 @@ const Operation: FC<OperationProps> = ({
<ActionButton onClick={() => handleFeedback('like')}> <ActionButton onClick={() => handleFeedback('like')}>
<RiThumbUpLine className='h-4 w-4' /> <RiThumbUpLine className='h-4 w-4' />
</ActionButton> </ActionButton>
<ActionButton onClick={() => handleFeedback('dislike')}> <ActionButton onClick={handleThumbsDown}>
<RiThumbDownLine className='h-4 w-4' /> <RiThumbDownLine className='h-4 w-4' />
</ActionButton> </ActionButton>
</> </>
@@ -188,6 +208,32 @@ const Operation: FC<OperationProps> = ({
createdAt={annotation?.created_at} createdAt={annotation?.created_at}
onRemove={() => onAnnotationRemoved?.(index)} onRemove={() => onAnnotationRemoved?.(index)}
/> />
{isShowFeedbackModal && (
<Modal
title={t('common.feedback.title') || 'Provide Feedback'}
subTitle={t('common.feedback.subtitle') || 'Please tell us what went wrong with this response'}
onClose={handleFeedbackCancel}
onConfirm={handleFeedbackSubmit}
onCancel={handleFeedbackCancel}
confirmButtonText={t('common.operation.submit') || 'Submit'}
cancelButtonText={t('common.operation.cancel') || 'Cancel'}
>
<div className='space-y-3'>
<div>
<label className='system-sm-semibold mb-2 block text-text-secondary'>
{t('common.feedback.content') || 'Feedback Content'}
</label>
<Textarea
value={feedbackContent}
onChange={e => setFeedbackContent(e.target.value)}
placeholder={t('common.feedback.placeholder') || 'Please describe what went wrong or how we can improve...'}
rows={4}
className='w-full'
/>
</div>
</div>
</Modal>
)}
</> </>
) )
} }

View File

@@ -390,7 +390,7 @@ export const useEmbeddedChatbot = () => {
}, [mutateAppConversationData, handleConversationIdInfoChange]) }, [mutateAppConversationData, handleConversationIdInfoChange])
const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => { const handleFeedback = useCallback(async (messageId: string, feedback: Feedback) => {
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, appId) await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating, content: feedback.content } }, isInstalledApp, appId)
notify({ type: 'success', message: t('common.api.success') }) notify({ type: 'success', message: t('common.api.success') })
}, [isInstalledApp, appId, t, notify]) }, [isInstalledApp, appId, t, notify])

View File

@@ -93,4 +93,5 @@ export type Callback = {
export type Feedback = { export type Feedback = {
rating: 'like' | 'dislike' | null rating: 'like' | 'dislike' | null
content?: string | null
} }

View File

@@ -102,7 +102,7 @@ const Result: FC<IResultProps> = ({
}) })
const handleFeedback = async (feedback: FeedbackType) => { const handleFeedback = async (feedback: FeedbackType) => {
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } }, isInstalledApp, installedAppInfo?.id) await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating, content: feedback.content } }, isInstalledApp, installedAppInfo?.id)
setFeedback(feedback) setFeedback(feedback)
} }

View File

@@ -319,6 +319,12 @@ const translation = {
transfer: 'Transfer workspace ownership', transfer: 'Transfer workspace ownership',
}, },
}, },
feedback: {
title: 'Provide Feedback',
subtitle: 'Please tell us what went wrong with this response',
content: 'Feedback Content',
placeholder: 'Please describe what went wrong or how we can improve...',
},
integrations: { integrations: {
connected: 'Connected', connected: 'Connected',
google: 'Google', google: 'Google',