new-segment-modal.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { memo, useState } from 'react'
  2. import type { FC } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import { useContext } from 'use-context-selector'
  5. import { useParams } from 'next/navigation'
  6. import { RiCloseLine } from '@remixicon/react'
  7. import Modal from '@/app/components/base/modal'
  8. import Button from '@/app/components/base/button'
  9. import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/common'
  10. import { Hash02 } from '@/app/components/base/icons/src/vender/line/general'
  11. import { ToastContext } from '@/app/components/base/toast'
  12. import type { SegmentUpdater } from '@/models/datasets'
  13. import { addSegment } from '@/service/datasets'
  14. import TagInput from '@/app/components/base/tag-input'
  15. type NewSegmentModalProps = {
  16. isShow: boolean
  17. onCancel: () => void
  18. docForm: string
  19. onSave: () => void
  20. }
  21. const NewSegmentModal: FC<NewSegmentModalProps> = ({
  22. isShow,
  23. onCancel,
  24. docForm,
  25. onSave,
  26. }) => {
  27. const { t } = useTranslation()
  28. const { notify } = useContext(ToastContext)
  29. const [question, setQuestion] = useState('')
  30. const [answer, setAnswer] = useState('')
  31. const { datasetId, documentId } = useParams()
  32. const [keywords, setKeywords] = useState<string[]>([])
  33. const [loading, setLoading] = useState(false)
  34. const handleCancel = () => {
  35. setQuestion('')
  36. setAnswer('')
  37. onCancel()
  38. setKeywords([])
  39. }
  40. const handleSave = async () => {
  41. const params: SegmentUpdater = { content: '' }
  42. if (docForm === 'qa_model') {
  43. if (!question.trim())
  44. return notify({ type: 'error', message: t('datasetDocuments.segment.questionEmpty') })
  45. if (!answer.trim())
  46. return notify({ type: 'error', message: t('datasetDocuments.segment.answerEmpty') })
  47. params.content = question
  48. params.answer = answer
  49. }
  50. else {
  51. if (!question.trim())
  52. return notify({ type: 'error', message: t('datasetDocuments.segment.contentEmpty') })
  53. params.content = question
  54. }
  55. if (keywords?.length)
  56. params.keywords = keywords
  57. setLoading(true)
  58. try {
  59. await addSegment({ datasetId, documentId, body: params })
  60. notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
  61. handleCancel()
  62. onSave()
  63. }
  64. finally {
  65. setLoading(false)
  66. }
  67. }
  68. const renderContent = () => {
  69. if (docForm === 'qa_model') {
  70. return (
  71. <>
  72. <div className='mb-1 text-xs font-medium text-gray-500'>QUESTION</div>
  73. <AutoHeightTextarea
  74. outerClassName='mb-4'
  75. className='leading-6 text-md text-gray-800'
  76. value={question}
  77. placeholder={t('datasetDocuments.segment.questionPlaceholder') || ''}
  78. onChange={e => setQuestion(e.target.value)}
  79. autoFocus
  80. />
  81. <div className='mb-1 text-xs font-medium text-gray-500'>ANSWER</div>
  82. <AutoHeightTextarea
  83. outerClassName='mb-4'
  84. className='leading-6 text-md text-gray-800'
  85. value={answer}
  86. placeholder={t('datasetDocuments.segment.answerPlaceholder') || ''}
  87. onChange={e => setAnswer(e.target.value)}
  88. />
  89. </>
  90. )
  91. }
  92. return (
  93. <AutoHeightTextarea
  94. className='leading-6 text-md text-gray-800'
  95. value={question}
  96. placeholder={t('datasetDocuments.segment.contentPlaceholder') || ''}
  97. onChange={e => setQuestion(e.target.value)}
  98. autoFocus
  99. />
  100. )
  101. }
  102. return (
  103. <Modal isShow={isShow} onClose={() => { }} className='pt-8 px-8 pb-6 !max-w-[640px] !rounded-xl'>
  104. <div className={'flex flex-col relative'}>
  105. <div className='absolute right-0 -top-0.5 flex items-center h-6'>
  106. <div className='flex justify-center items-center w-6 h-6 cursor-pointer' onClick={handleCancel}>
  107. <RiCloseLine className='w-4 h-4 text-gray-500' />
  108. </div>
  109. </div>
  110. <div className='mb-[14px]'>
  111. <span className='inline-flex items-center px-1.5 h-5 border border-gray-200 rounded-md'>
  112. <Hash02 className='mr-0.5 w-3 h-3 text-gray-400' />
  113. <span className='text-[11px] font-medium text-gray-500 italic'>
  114. {
  115. docForm === 'qa_model'
  116. ? t('datasetDocuments.segment.newQaSegment')
  117. : t('datasetDocuments.segment.newTextSegment')
  118. }
  119. </span>
  120. </span>
  121. </div>
  122. <div className='mb-4 py-1.5 h-[420px] overflow-auto'>{renderContent()}</div>
  123. <div className='text-xs font-medium text-gray-500'>{t('datasetDocuments.segment.keywords')}</div>
  124. <div className='mb-8'>
  125. <TagInput items={keywords} onChange={newKeywords => setKeywords(newKeywords)} />
  126. </div>
  127. <div className='flex justify-end'>
  128. <Button
  129. onClick={handleCancel}>
  130. {t('common.operation.cancel')}
  131. </Button>
  132. <Button
  133. variant='primary'
  134. onClick={handleSave}
  135. disabled={loading}
  136. >
  137. {t('common.operation.save')}
  138. </Button>
  139. </div>
  140. </div>
  141. </Modal>
  142. )
  143. }
  144. export default memo(NewSegmentModal)