| @@ -28,6 +28,7 @@ import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/commo | |||
| import Button from '@/app/components/base/button' | |||
| import NewSegmentModal from '@/app/components/datasets/documents/detail/new-segment-modal' | |||
| 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 }) => { | |||
| const localPositionId = useMemo(() => { | |||
| @@ -66,6 +67,15 @@ export const SegmentDetail: FC<ISegmentDetailProps> = memo(({ | |||
| const [question, setQuestion] = useState(segInfo?.content || '') | |||
| const [answer, setAnswer] = useState(segInfo?.answer || '') | |||
| 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 = () => { | |||
| setIsEditing(false) | |||
| @@ -129,7 +139,9 @@ export const SegmentDetail: FC<ISegmentDetailProps> = memo(({ | |||
| <Button | |||
| type='primary' | |||
| className='!h-7 !px-3 !py-[5px] text-xs font-medium !rounded-md' | |||
| onClick={handleSave}> | |||
| onClick={handleSave} | |||
| disabled={loading} | |||
| > | |||
| {t('common.operation.save')} | |||
| </Button> | |||
| </> | |||
| @@ -225,6 +237,7 @@ const Completed: FC<ICompletedProps> = ({ | |||
| const [allSegments, setAllSegments] = useState<Array<SegmentDetailModel[]>>([]) // all segments data | |||
| const [loading, setLoading] = useState(false) | |||
| const [total, setTotal] = useState<number | undefined>() | |||
| const { eventEmitter } = useEventEmitterContextContext() | |||
| const onChangeStatus = ({ value }: Item) => { | |||
| setSelectedStatus(value === 'all' ? 'all' : !!value) | |||
| @@ -318,23 +331,29 @@ const Completed: FC<ICompletedProps> = ({ | |||
| if (keywords.length) | |||
| 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(() => { | |||
| @@ -31,6 +31,7 @@ const NewSegmentModal: FC<NewSegmentModalProps> = memo(({ | |||
| const [answer, setAnswer] = useState('') | |||
| const { datasetId, documentId } = useParams() | |||
| const [keywords, setKeywords] = useState<string[]>([]) | |||
| const [loading, setLoading] = useState(false) | |||
| const handleCancel = () => { | |||
| setQuestion('') | |||
| @@ -60,10 +61,16 @@ const NewSegmentModal: FC<NewSegmentModalProps> = memo(({ | |||
| if (keywords?.length) | |||
| 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 = () => { | |||
| @@ -136,7 +143,9 @@ const NewSegmentModal: FC<NewSegmentModalProps> = memo(({ | |||
| <Button | |||
| type='primary' | |||
| className='!h-9 !px-4 !py-2 text-sm font-medium !rounded-lg' | |||
| onClick={handleSave}> | |||
| onClick={handleSave} | |||
| disabled={loading} | |||
| > | |||
| {t('common.operation.save')} | |||
| </Button> | |||
| </div> | |||
| @@ -27,7 +27,6 @@ import NotionIcon from '@/app/components/base/notion-icon' | |||
| import ProgressBar from '@/app/components/base/progress-bar' | |||
| import { DataSourceType, type DocumentDisplayStatus, type SimpleDocumentDetail } from '@/models/datasets' | |||
| 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' | |||
| export const SettingsIcon: FC<{ className?: string }> = ({ className }) => { | |||
| @@ -89,7 +88,9 @@ export const StatusItem: FC<{ | |||
| errorMessage && ( | |||
| <Tooltip | |||
| 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' /> | |||
| </Tooltip> | |||
| @@ -152,73 +153,6 @@ export const OperationAction: FC<{ | |||
| 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()}> | |||
| {isListScene && <> | |||
| {archived | |||