operation.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import type { FC } from 'react'
  2. import {
  3. memo,
  4. useMemo,
  5. useState,
  6. } from 'react'
  7. import { useTranslation } from 'react-i18next'
  8. import type { ChatItem } from '../../types'
  9. import { useChatContext } from '../context'
  10. import CopyBtn from '@/app/components/app/chat/copy-btn'
  11. import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
  12. import AudioBtn from '@/app/components/base/audio-btn'
  13. import AnnotationCtrlBtn from '@/app/components/app/configuration/toolbox/annotation/annotation-ctrl-btn'
  14. import EditReplyModal from '@/app/components/app/annotation/edit-annotation-modal'
  15. import {
  16. ThumbsDown,
  17. ThumbsUp,
  18. } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
  19. import TooltipPlus from '@/app/components/base/tooltip-plus'
  20. type OperationProps = {
  21. item: ChatItem
  22. question: string
  23. index: number
  24. }
  25. const Operation: FC<OperationProps> = ({
  26. item,
  27. question,
  28. index,
  29. }) => {
  30. const { t } = useTranslation()
  31. const {
  32. config,
  33. onAnnotationAdded,
  34. onAnnotationEdited,
  35. onAnnotationRemoved,
  36. onFeedback,
  37. } = useChatContext()
  38. const [isShowReplyModal, setIsShowReplyModal] = useState(false)
  39. const {
  40. id,
  41. isOpeningStatement,
  42. content: messageContent,
  43. annotation,
  44. feedback,
  45. agent_thoughts,
  46. } = item
  47. const hasAnnotation = !!annotation?.id
  48. const [localFeedback, setLocalFeedback] = useState(feedback)
  49. const content = useMemo(() => {
  50. if (agent_thoughts?.length)
  51. return agent_thoughts.reduce((acc, cur) => acc + cur.thought, '')
  52. return messageContent
  53. }, [agent_thoughts, messageContent])
  54. const handleFeedback = async (rating: 'like' | 'dislike' | null) => {
  55. if (!config?.supportFeedback || !onFeedback)
  56. return
  57. await onFeedback?.(id, { rating })
  58. setLocalFeedback({ rating })
  59. }
  60. return (
  61. <div className='absolute top-[-14px] right-[-14px] flex justify-end gap-1'>
  62. {
  63. !isOpeningStatement && (
  64. <CopyBtn
  65. value={content}
  66. className='hidden group-hover:block'
  67. />
  68. )
  69. }
  70. {(!isOpeningStatement && config?.text_to_speech?.enabled) && (
  71. <AudioBtn
  72. value={content}
  73. className='hidden group-hover:block'
  74. />
  75. )}
  76. {(!isOpeningStatement && config?.supportAnnotation && config.annotation_reply?.enabled) && (
  77. <AnnotationCtrlBtn
  78. appId={config?.appId || ''}
  79. messageId={id}
  80. annotationId={annotation?.id || ''}
  81. className='hidden group-hover:block ml-1 shrink-0'
  82. cached={hasAnnotation}
  83. query={question}
  84. answer={content}
  85. onAdded={(id, authorName) => onAnnotationAdded?.(id, authorName, question, content, index)}
  86. onEdit={() => setIsShowReplyModal(true)}
  87. onRemoved={() => onAnnotationRemoved?.(index)}
  88. />
  89. )}
  90. <EditReplyModal
  91. isShow={isShowReplyModal}
  92. onHide={() => setIsShowReplyModal(false)}
  93. query={question}
  94. answer={content}
  95. onEdited={(editedQuery, editedAnswer) => onAnnotationEdited?.(editedQuery, editedAnswer, index)}
  96. onAdded={(annotationId, authorName, editedQuery, editedAnswer) => onAnnotationAdded?.(annotationId, authorName, editedQuery, editedAnswer, index)}
  97. appId={config?.appId || ''}
  98. messageId={id}
  99. annotationId={annotation?.id || ''}
  100. createdAt={annotation?.created_at}
  101. onRemove={() => onAnnotationRemoved?.(index)}
  102. />
  103. {
  104. annotation?.id && (
  105. <div
  106. className='relative box-border flex items-center justify-center h-7 w-7 p-0.5 rounded-lg bg-white cursor-pointer text-[#444CE7] shadow-md'
  107. >
  108. <div className='p-1 rounded-lg bg-[#EEF4FF] '>
  109. <MessageFast className='w-4 h-4' />
  110. </div>
  111. </div>
  112. )
  113. }
  114. {
  115. config?.supportFeedback && !localFeedback?.rating && onFeedback && !isOpeningStatement && (
  116. <div className='hidden group-hover:flex ml-1 shrink-0 items-center px-0.5 bg-white border-[0.5px] border-gray-100 shadow-md text-gray-500 rounded-lg'>
  117. <TooltipPlus popupContent={t('appDebug.operation.agree')}>
  118. <div
  119. className='flex items-center justify-center mr-0.5 w-6 h-6 rounded-md hover:bg-black/5 hover:text-gray-800 cursor-pointer'
  120. onClick={() => handleFeedback('like')}
  121. >
  122. <ThumbsUp className='w-4 h-4' />
  123. </div>
  124. </TooltipPlus>
  125. <TooltipPlus popupContent={t('appDebug.operation.disagree')}>
  126. <div
  127. className='flex items-center justify-center w-6 h-6 rounded-md hover:bg-black/5 hover:text-gray-800 cursor-pointer'
  128. onClick={() => handleFeedback('dislike')}
  129. >
  130. <ThumbsDown className='w-4 h-4' />
  131. </div>
  132. </TooltipPlus>
  133. </div>
  134. )
  135. }
  136. {
  137. config?.supportFeedback && localFeedback?.rating && onFeedback && !isOpeningStatement && (
  138. <TooltipPlus popupContent={localFeedback.rating === 'like' ? t('appDebug.operation.cancelAgree') : t('appDebug.operation.cancelDisagree')}>
  139. <div
  140. className={`
  141. flex items-center justify-center w-7 h-7 rounded-[10px] border-[2px] border-white cursor-pointer
  142. ${localFeedback.rating === 'like' && 'bg-blue-50 text-blue-600'}
  143. ${localFeedback.rating === 'dislike' && 'bg-red-100 text-red-600'}
  144. `}
  145. onClick={() => handleFeedback(null)}
  146. >
  147. {
  148. localFeedback.rating === 'like' && (
  149. <ThumbsUp className='w-4 h-4' />
  150. )
  151. }
  152. {
  153. localFeedback.rating === 'dislike' && (
  154. <ThumbsDown className='w-4 h-4' />
  155. )
  156. }
  157. </div>
  158. </TooltipPlus>
  159. )
  160. }
  161. </div>
  162. )
  163. }
  164. export default memo(Operation)