| import Button from '@/app/components/base/button' | import Button from '@/app/components/base/button' | ||||
| import NewSegmentModal from '@/app/components/datasets/documents/detail/new-segment-modal' | import NewSegmentModal from '@/app/components/datasets/documents/detail/new-segment-modal' | ||||
| import TagInput from '@/app/components/base/tag-input' | import TagInput from '@/app/components/base/tag-input' | ||||
| import { useEventEmitterContextContext } from '@/context/event-emitter' | |||||
| export const SegmentIndexTag: FC<{ positionId: string | number; className?: string }> = ({ positionId, className }) => { | export const SegmentIndexTag: FC<{ positionId: string | number; className?: string }> = ({ positionId, className }) => { | ||||
| const localPositionId = useMemo(() => { | const localPositionId = useMemo(() => { | ||||
| const [question, setQuestion] = useState(segInfo?.content || '') | const [question, setQuestion] = useState(segInfo?.content || '') | ||||
| const [answer, setAnswer] = useState(segInfo?.answer || '') | const [answer, setAnswer] = useState(segInfo?.answer || '') | ||||
| const [keywords, setKeywords] = useState<string[]>(segInfo?.keywords || []) | const [keywords, setKeywords] = useState<string[]>(segInfo?.keywords || []) | ||||
| const { eventEmitter } = useEventEmitterContextContext() | |||||
| const [loading, setLoading] = useState(false) | |||||
| eventEmitter?.useSubscription((v) => { | |||||
| if (v === 'update-segment') | |||||
| setLoading(true) | |||||
| else | |||||
| setLoading(false) | |||||
| }) | |||||
| const handleCancel = () => { | const handleCancel = () => { | ||||
| setIsEditing(false) | setIsEditing(false) | ||||
| <Button | <Button | ||||
| type='primary' | type='primary' | ||||
| className='!h-7 !px-3 !py-[5px] text-xs font-medium !rounded-md' | className='!h-7 !px-3 !py-[5px] text-xs font-medium !rounded-md' | ||||
| onClick={handleSave}> | |||||
| onClick={handleSave} | |||||
| disabled={loading} | |||||
| > | |||||
| {t('common.operation.save')} | {t('common.operation.save')} | ||||
| </Button> | </Button> | ||||
| </> | </> | ||||
| const [allSegments, setAllSegments] = useState<Array<SegmentDetailModel[]>>([]) // all segments data | const [allSegments, setAllSegments] = useState<Array<SegmentDetailModel[]>>([]) // all segments data | ||||
| const [loading, setLoading] = useState(false) | const [loading, setLoading] = useState(false) | ||||
| const [total, setTotal] = useState<number | undefined>() | const [total, setTotal] = useState<number | undefined>() | ||||
| const { eventEmitter } = useEventEmitterContextContext() | |||||
| const onChangeStatus = ({ value }: Item) => { | const onChangeStatus = ({ value }: Item) => { | ||||
| setSelectedStatus(value === 'all' ? 'all' : !!value) | setSelectedStatus(value === 'all' ? 'all' : !!value) | ||||
| if (keywords.length) | if (keywords.length) | ||||
| params.keywords = keywords | params.keywords = keywords | ||||
| const res = await updateSegment({ datasetId, documentId, segmentId, body: params }) | |||||
| notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) | |||||
| onCloseModal() | |||||
| for (const item of allSegments) { | |||||
| for (const seg of item) { | |||||
| if (seg.id === segmentId) { | |||||
| seg.answer = res.data.answer | |||||
| seg.content = res.data.content | |||||
| seg.keywords = res.data.keywords | |||||
| seg.word_count = res.data.word_count | |||||
| seg.hit_count = res.data.hit_count | |||||
| seg.index_node_hash = res.data.index_node_hash | |||||
| seg.enabled = res.data.enabled | |||||
| try { | |||||
| eventEmitter?.emit('update-segment') | |||||
| const res = await updateSegment({ datasetId, documentId, segmentId, body: params }) | |||||
| notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) | |||||
| onCloseModal() | |||||
| for (const item of allSegments) { | |||||
| for (const seg of item) { | |||||
| if (seg.id === segmentId) { | |||||
| seg.answer = res.data.answer | |||||
| seg.content = res.data.content | |||||
| seg.keywords = res.data.keywords | |||||
| seg.word_count = res.data.word_count | |||||
| seg.hit_count = res.data.hit_count | |||||
| seg.index_node_hash = res.data.index_node_hash | |||||
| seg.enabled = res.data.enabled | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| setAllSegments([...allSegments]) | |||||
| } | |||||
| finally { | |||||
| eventEmitter?.emit('') | |||||
| } | } | ||||
| setAllSegments([...allSegments]) | |||||
| } | } | ||||
| useEffect(() => { | useEffect(() => { |
| const [answer, setAnswer] = useState('') | const [answer, setAnswer] = useState('') | ||||
| const { datasetId, documentId } = useParams() | const { datasetId, documentId } = useParams() | ||||
| const [keywords, setKeywords] = useState<string[]>([]) | const [keywords, setKeywords] = useState<string[]>([]) | ||||
| const [loading, setLoading] = useState(false) | |||||
| const handleCancel = () => { | const handleCancel = () => { | ||||
| setQuestion('') | setQuestion('') | ||||
| if (keywords?.length) | if (keywords?.length) | ||||
| params.keywords = keywords | params.keywords = keywords | ||||
| await addSegment({ datasetId, documentId, body: params }) | |||||
| notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) | |||||
| handleCancel() | |||||
| onSave() | |||||
| setLoading(true) | |||||
| try { | |||||
| await addSegment({ datasetId, documentId, body: params }) | |||||
| notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) | |||||
| handleCancel() | |||||
| onSave() | |||||
| } | |||||
| finally { | |||||
| setLoading(false) | |||||
| } | |||||
| } | } | ||||
| const renderContent = () => { | const renderContent = () => { | ||||
| <Button | <Button | ||||
| type='primary' | type='primary' | ||||
| className='!h-9 !px-4 !py-2 text-sm font-medium !rounded-lg' | className='!h-9 !px-4 !py-2 text-sm font-medium !rounded-lg' | ||||
| onClick={handleSave}> | |||||
| onClick={handleSave} | |||||
| disabled={loading} | |||||
| > | |||||
| {t('common.operation.save')} | {t('common.operation.save')} | ||||
| </Button> | </Button> | ||||
| </div> | </div> |
| import ProgressBar from '@/app/components/base/progress-bar' | import ProgressBar from '@/app/components/base/progress-bar' | ||||
| import { DataSourceType, type DocumentDisplayStatus, type SimpleDocumentDetail } from '@/models/datasets' | import { DataSourceType, type DocumentDisplayStatus, type SimpleDocumentDetail } from '@/models/datasets' | ||||
| import type { CommonResponse } from '@/models/common' | import type { CommonResponse } from '@/models/common' | ||||
| import { FilePlus02 } from '@/app/components/base/icons/src/vender/line/files' | |||||
| import { DotsHorizontal, HelpCircle } from '@/app/components/base/icons/src/vender/line/general' | import { DotsHorizontal, HelpCircle } from '@/app/components/base/icons/src/vender/line/general' | ||||
| export const SettingsIcon: FC<{ className?: string }> = ({ className }) => { | export const SettingsIcon: FC<{ className?: string }> = ({ className }) => { | ||||
| errorMessage && ( | errorMessage && ( | ||||
| <Tooltip | <Tooltip | ||||
| selector='dataset-document-detail-item-status' | selector='dataset-document-detail-item-status' | ||||
| content={errorMessage} | |||||
| htmlContent={ | |||||
| <div className='max-w-[260px]'>{errorMessage}</div> | |||||
| } | |||||
| > | > | ||||
| <HelpCircle className='ml-1 w-[14px] h-[14px] text-gray-700' /> | <HelpCircle className='ml-1 w-[14px] h-[14px] text-gray-700' /> | ||||
| </Tooltip> | </Tooltip> | ||||
| onUpdate(operationName) | onUpdate(operationName) | ||||
| } | } | ||||
| const Operations = (props: any) => <div className='w-full py-1'> | |||||
| {!isListScene && <> | |||||
| <div className='flex justify-between items-center mx-4 pt-2'> | |||||
| <span className={cn(s.actionName, 'font-medium')}> | |||||
| {!archived && enabled ? t('datasetDocuments.list.index.enable') : t('datasetDocuments.list.index.disable')} | |||||
| </span> | |||||
| <Tooltip | |||||
| selector={`detail-switch-${id}`} | |||||
| content={t('datasetDocuments.list.action.enableWarning') as string} | |||||
| className='!font-semibold' | |||||
| disabled={!archived} | |||||
| > | |||||
| <div> | |||||
| <Switch | |||||
| defaultValue={archived ? false : enabled} | |||||
| onChange={v => !archived && onOperate(v ? 'enable' : 'disable')} | |||||
| disabled={archived} | |||||
| size='md' | |||||
| /> | |||||
| </div> | |||||
| </Tooltip> | |||||
| </div> | |||||
| <div className='mx-4 pb-1 pt-0.5 text-xs text-gray-500'> | |||||
| {!archived && enabled ? t('datasetDocuments.list.index.enableTip') : t('datasetDocuments.list.index.disableTip')} | |||||
| </div> | |||||
| <Divider /> | |||||
| </>} | |||||
| {!archived && ( | |||||
| <> | |||||
| <div className={s.actionItem} onClick={() => router.push(`/datasets/${datasetId}/documents/${detail.id}/settings`)}> | |||||
| <SettingsIcon /> | |||||
| <span className={s.actionName}>{t('datasetDocuments.list.action.settings')}</span> | |||||
| </div> | |||||
| { | |||||
| !isListScene && ( | |||||
| <div className={s.actionItem} onClick={showNewSegmentModal}> | |||||
| <FilePlus02 className='w-4 h-4 text-gray-500' /> | |||||
| <span className={s.actionName}>{t('datasetDocuments.list.action.add')}</span> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| { | |||||
| data_source_type === 'notion_import' && ( | |||||
| <div className={s.actionItem} onClick={() => onOperate('sync')}> | |||||
| <SyncIcon /> | |||||
| <span className={s.actionName}>{t('datasetDocuments.list.action.sync')}</span> | |||||
| </div> | |||||
| ) | |||||
| } | |||||
| <Divider className='my-1' /> | |||||
| </> | |||||
| )} | |||||
| {!archived && <div className={s.actionItem} onClick={() => onOperate('archive')}> | |||||
| <ArchiveIcon /> | |||||
| <span className={s.actionName}>{t('datasetDocuments.list.action.archive')}</span> | |||||
| </div>} | |||||
| <div | |||||
| className={cn(s.actionItem, s.deleteActionItem, 'group')} | |||||
| onClick={() => { | |||||
| setShowModal(true) | |||||
| props?.onClose() | |||||
| }}> | |||||
| <TrashIcon className={'w-4 h-4 stroke-current text-gray-500 stroke-2 group-hover:text-red-500'} /> | |||||
| <span className={cn(s.actionName, 'group-hover:text-red-500')}>{t('datasetDocuments.list.action.delete')}</span> | |||||
| </div> | |||||
| </div> | |||||
| return <div className='flex items-center' onClick={e => e.stopPropagation()}> | return <div className='flex items-center' onClick={e => e.stopPropagation()}> | ||||
| {isListScene && <> | {isListScene && <> | ||||
| {archived | {archived |