* feat: add hooks for document table * refactor: refactor document-related modaltags/v0.1.0
| @@ -1,8 +1,10 @@ | |||
| import { IChunk } from '@/interfaces/database/knowledge'; | |||
| import { IChunk, IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { buildChunkHighlights } from '@/utils/documentUtils'; | |||
| import { useMemo } from 'react'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| import { IHighlight } from 'react-pdf-highlighter'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| import { useGetKnowledgeSearchParams } from './routeHook'; | |||
| export const useGetDocumentUrl = (documentId: string) => { | |||
| const url = useMemo(() => { | |||
| @@ -19,3 +21,139 @@ export const useGetChunkHighlights = (selectedChunk: IChunk): IHighlight[] => { | |||
| return highlights; | |||
| }; | |||
| export const useFetchDocumentList = () => { | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const dispatch = useDispatch(); | |||
| const fetchKfList = useCallback(() => { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/getKfList', | |||
| payload: { | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| }, [dispatch, knowledgeId]); | |||
| return fetchKfList; | |||
| }; | |||
| export const useSetDocumentStatus = () => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const setDocumentStatus = useCallback( | |||
| (status: boolean, documentId: string) => { | |||
| dispatch({ | |||
| type: 'kFModel/updateDocumentStatus', | |||
| payload: { | |||
| doc_id: documentId, | |||
| status: Number(status), | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| return setDocumentStatus; | |||
| }; | |||
| export const useSelectDocumentList = () => { | |||
| const list: IKnowledgeFile[] = useSelector( | |||
| (state: any) => state.kFModel.data, | |||
| ); | |||
| return list; | |||
| }; | |||
| export const useSaveDocumentName = () => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const saveName = useCallback( | |||
| (documentId: string, name: string) => { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_rename', | |||
| payload: { | |||
| doc_id: documentId, | |||
| name: name, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| return saveName; | |||
| }; | |||
| export const useCreateDocument = () => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const createDocument = useCallback( | |||
| (name: string) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_create', | |||
| payload: { | |||
| name, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| return createDocument; | |||
| }; | |||
| export const useSetDocumentParser = () => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const setDocumentParser = useCallback( | |||
| (parserId: string, documentId: string) => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_change_parser', | |||
| payload: { | |||
| parser_id: parserId, | |||
| doc_id: documentId, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }, | |||
| [dispatch, knowledgeId], | |||
| ); | |||
| return setDocumentParser; | |||
| }; | |||
| export const useRemoveDocument = (documentId: string) => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const removeDocument = useCallback(() => { | |||
| try { | |||
| return dispatch<any>({ | |||
| type: 'kFModel/document_rm', | |||
| payload: { | |||
| doc_id: documentId, | |||
| kb_id: knowledgeId, | |||
| }, | |||
| }); | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }, [dispatch, knowledgeId, documentId]); | |||
| return removeDocument; | |||
| }; | |||
| @@ -1,86 +1,69 @@ | |||
| import { | |||
| useFetchTenantInfo, | |||
| useSelectParserList, | |||
| } from '@/hooks/userSettingHook'; | |||
| import { Modal, Space, Tag } from 'antd'; | |||
| import React, { useEffect, useState } from 'react'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| import styles from './index.less'; | |||
| const { CheckableTag } = Tag; | |||
| interface kFProps { | |||
| getKfList: () => void; | |||
| parser_id: string; | |||
| doc_id: string; | |||
| } | |||
| const SegmentSetModal: React.FC<kFProps> = ({ | |||
| getKfList, | |||
| parser_id, | |||
| doc_id, | |||
| }) => { | |||
| const dispatch = useDispatch(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const [selectedTag, setSelectedTag] = useState(''); | |||
| const { isShowSegmentSetModal } = kFModel; | |||
| const parserList = useSelectParserList(); | |||
| useFetchTenantInfo(); | |||
| useEffect(() => { | |||
| setSelectedTag(parser_id); | |||
| }, [parser_id]); | |||
| const handleCancel = () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowSegmentSetModal: false, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleOk = async () => { | |||
| const retcode = await dispatch<any>({ | |||
| type: 'kFModel/document_change_parser', | |||
| payload: { | |||
| parser_id: selectedTag, | |||
| doc_id, | |||
| }, | |||
| }); | |||
| if (retcode === 0 && getKfList) { | |||
| getKfList(); | |||
| handleCancel(); | |||
| } | |||
| }; | |||
| const handleChange = (tag: string, checked: boolean) => { | |||
| const nextSelectedTag = checked ? tag : selectedTag; | |||
| setSelectedTag(nextSelectedTag); | |||
| }; | |||
| return ( | |||
| <Modal | |||
| title="Category" | |||
| open={isShowSegmentSetModal} | |||
| onOk={handleOk} | |||
| onCancel={handleCancel} | |||
| > | |||
| <Space size={[0, 8]} wrap> | |||
| <div className={styles.tags}> | |||
| {parserList.map((x) => { | |||
| return ( | |||
| <CheckableTag | |||
| key={x.value} | |||
| checked={selectedTag === x.value} | |||
| onChange={(checked) => handleChange(x.value, checked)} | |||
| > | |||
| {x.label} | |||
| </CheckableTag> | |||
| ); | |||
| })} | |||
| </div> | |||
| </Space> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default SegmentSetModal; | |||
| import { IModalManagerChildrenProps } from '@/components/modal-manager'; | |||
| import { | |||
| useFetchTenantInfo, | |||
| useSelectParserList, | |||
| } from '@/hooks/userSettingHook'; | |||
| import { Modal, Space, Tag } from 'antd'; | |||
| import React, { useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| const { CheckableTag } = Tag; | |||
| interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> { | |||
| loading: boolean; | |||
| onOk: (parserId: string) => void; | |||
| showModal?(): void; | |||
| parser_id: string; | |||
| } | |||
| const ChunkMethodModal: React.FC<IProps> = ({ | |||
| parser_id, | |||
| onOk, | |||
| hideModal, | |||
| visible, | |||
| }) => { | |||
| const [selectedTag, setSelectedTag] = useState(''); | |||
| const parserList = useSelectParserList(); | |||
| useFetchTenantInfo(); | |||
| useEffect(() => { | |||
| setSelectedTag(parser_id); | |||
| }, [parser_id]); | |||
| const handleOk = async () => { | |||
| onOk(selectedTag); | |||
| }; | |||
| const handleChange = (tag: string, checked: boolean) => { | |||
| const nextSelectedTag = checked ? tag : selectedTag; | |||
| setSelectedTag(nextSelectedTag); | |||
| }; | |||
| return ( | |||
| <Modal | |||
| title="Chunk Method" | |||
| open={visible} | |||
| onOk={handleOk} | |||
| onCancel={hideModal} | |||
| > | |||
| <Space size={[0, 8]} wrap> | |||
| <div className={styles.tags}> | |||
| {parserList.map((x) => { | |||
| return ( | |||
| <CheckableTag | |||
| key={x.value} | |||
| checked={selectedTag === x.value} | |||
| onChange={(checked) => handleChange(x.value, checked)} | |||
| > | |||
| {x.label} | |||
| </CheckableTag> | |||
| ); | |||
| })} | |||
| </div> | |||
| </Space> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default ChunkMethodModal; | |||
| @@ -0,0 +1,49 @@ | |||
| import { IModalManagerChildrenProps } from '@/components/modal-manager'; | |||
| import { Form, Input, Modal } from 'antd'; | |||
| import React from 'react'; | |||
| type FieldType = { | |||
| name?: string; | |||
| }; | |||
| interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> { | |||
| loading: boolean; | |||
| onOk: (name: string) => void; | |||
| showModal?(): void; | |||
| } | |||
| const FileCreatingModal: React.FC<IProps> = ({ visible, hideModal, onOk }) => { | |||
| const [form] = Form.useForm(); | |||
| const handleOk = async () => { | |||
| const values = await form.validateFields(); | |||
| onOk(values.name); | |||
| }; | |||
| return ( | |||
| <Modal | |||
| title="File Name" | |||
| open={visible} | |||
| onOk={handleOk} | |||
| onCancel={hideModal} | |||
| > | |||
| <Form | |||
| form={form} | |||
| name="validateOnly" | |||
| labelCol={{ span: 4 }} | |||
| wrapperCol={{ span: 20 }} | |||
| style={{ maxWidth: 600 }} | |||
| autoComplete="off" | |||
| > | |||
| <Form.Item<FieldType> | |||
| label="File Name" | |||
| name="name" | |||
| rules={[{ required: true, message: 'Please input name!' }]} | |||
| > | |||
| <Input /> | |||
| </Form.Item> | |||
| </Form> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default FileCreatingModal; | |||
| @@ -1,78 +0,0 @@ | |||
| import { Form, Input, Modal } from 'antd'; | |||
| import React from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| type FieldType = { | |||
| name?: string; | |||
| }; | |||
| interface kFProps { | |||
| getKfList: () => void; | |||
| kb_id: string; | |||
| } | |||
| const FileCreatingModal: React.FC<kFProps> = ({ getKfList, kb_id }) => { | |||
| const dispatch = useDispatch(); | |||
| const [form] = Form.useForm(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const { isShowCEFwModal } = kFModel; | |||
| const { t } = useTranslation(); | |||
| const handleCancel = () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowCEFwModal: false, | |||
| }, | |||
| }); | |||
| }; | |||
| const createDocument = async () => { | |||
| try { | |||
| const values = await form.validateFields(); | |||
| const retcode = await dispatch<any>({ | |||
| type: 'kFModel/document_create', | |||
| payload: { | |||
| name: values.name, | |||
| kb_id, | |||
| }, | |||
| }); | |||
| if (retcode === 0) { | |||
| getKfList && getKfList(); | |||
| } | |||
| } catch (errorInfo) { | |||
| console.log('Failed:', errorInfo); | |||
| } | |||
| }; | |||
| const handleOk = async () => { | |||
| createDocument(); | |||
| }; | |||
| return ( | |||
| <Modal | |||
| title="File Name" | |||
| open={isShowCEFwModal} | |||
| onOk={handleOk} | |||
| onCancel={handleCancel} | |||
| > | |||
| <Form | |||
| form={form} | |||
| name="validateOnly" | |||
| labelCol={{ span: 4 }} | |||
| wrapperCol={{ span: 20 }} | |||
| style={{ maxWidth: 600 }} | |||
| autoComplete="off" | |||
| > | |||
| <Form.Item<FieldType> | |||
| label="File Name" | |||
| name="name" | |||
| rules={[{ required: true, message: 'Please input name!' }]} | |||
| > | |||
| <Input /> | |||
| </Form.Item> | |||
| </Form> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default FileCreatingModal; | |||
| @@ -0,0 +1,241 @@ | |||
| import { useSetModalState } from '@/hooks/commonHooks'; | |||
| import { | |||
| useCreateDocument, | |||
| useFetchDocumentList, | |||
| useSaveDocumentName, | |||
| useSetDocumentParser, | |||
| } from '@/hooks/documentHooks'; | |||
| import { useGetKnowledgeSearchParams } from '@/hooks/routeHook'; | |||
| import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; | |||
| import { useFetchTenantInfo } from '@/hooks/userSettingHook'; | |||
| import { Pagination } from '@/interfaces/common'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { PaginationProps } from 'antd'; | |||
| import { useCallback, useEffect, useMemo, useState } from 'react'; | |||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||
| import { KnowledgeRouteKey } from './constant'; | |||
| export const useFetchDocumentListOnMount = () => { | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const fetchDocumentList = useFetchDocumentList(); | |||
| const dispatch = useDispatch(); | |||
| useFetchTenantInfo(); | |||
| useEffect(() => { | |||
| if (knowledgeId) { | |||
| fetchDocumentList(); | |||
| dispatch({ | |||
| type: 'kFModel/pollGetDocumentList-start', | |||
| payload: knowledgeId, | |||
| }); | |||
| } | |||
| return () => { | |||
| dispatch({ | |||
| type: 'kFModel/pollGetDocumentList-stop', | |||
| }); | |||
| }; | |||
| }, [knowledgeId, dispatch, fetchDocumentList]); | |||
| return { fetchDocumentList }; | |||
| }; | |||
| export const useGetPagination = (fetchDocumentList: () => void) => { | |||
| const dispatch = useDispatch(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const setPagination = useCallback( | |||
| (pageNumber = 1, pageSize?: number) => { | |||
| const pagination: Pagination = { | |||
| current: pageNumber, | |||
| } as Pagination; | |||
| if (pageSize) { | |||
| pagination.pageSize = pageSize; | |||
| } | |||
| dispatch({ | |||
| type: 'kFModel/setPagination', | |||
| payload: pagination, | |||
| }); | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| const onPageChange: PaginationProps['onChange'] = useCallback( | |||
| (pageNumber: number, pageSize: number) => { | |||
| setPagination(pageNumber, pageSize); | |||
| fetchDocumentList(); | |||
| }, | |||
| [fetchDocumentList, setPagination], | |||
| ); | |||
| const pagination: PaginationProps = useMemo(() => { | |||
| return { | |||
| showQuickJumper: true, | |||
| total: kFModel.total, | |||
| showSizeChanger: true, | |||
| current: kFModel.pagination.currentPage, | |||
| pageSize: kFModel.pagination.pageSize, | |||
| pageSizeOptions: [1, 2, 10, 20, 50, 100], | |||
| onChange: onPageChange, | |||
| }; | |||
| }, [kFModel, onPageChange]); | |||
| return { | |||
| pagination, | |||
| setPagination, | |||
| total: kFModel.total, | |||
| searchString: kFModel.searchString, | |||
| }; | |||
| }; | |||
| export const useSelectDocumentListLoading = () => { | |||
| return useOneNamespaceEffectsLoading('kFModel', [ | |||
| 'getKfList', | |||
| 'updateDocumentStatus', | |||
| ]); | |||
| }; | |||
| export const useNavigateToOtherPage = () => { | |||
| const navigate = useNavigate(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const linkToUploadPage = useCallback(() => { | |||
| navigate(`/knowledge/dataset/upload?id=${knowledgeId}`); | |||
| }, [navigate, knowledgeId]); | |||
| const toChunk = useCallback( | |||
| (id: string) => { | |||
| navigate( | |||
| `/knowledge/${KnowledgeRouteKey.Dataset}/chunk?id=${knowledgeId}&doc_id=${id}`, | |||
| ); | |||
| }, | |||
| [navigate, knowledgeId], | |||
| ); | |||
| return { linkToUploadPage, toChunk }; | |||
| }; | |||
| export const useHandleSearchChange = (setPagination: () => void) => { | |||
| const dispatch = useDispatch(); | |||
| const { knowledgeId } = useGetKnowledgeSearchParams(); | |||
| const throttledGetDocumentList = useCallback(() => { | |||
| dispatch({ | |||
| type: 'kFModel/throttledGetDocumentList', | |||
| payload: knowledgeId, | |||
| }); | |||
| }, [dispatch, knowledgeId]); | |||
| const handleInputChange = useCallback( | |||
| (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { | |||
| const value = e.target.value; | |||
| dispatch({ type: 'kFModel/setSearchString', payload: value }); | |||
| setPagination(); | |||
| throttledGetDocumentList(); | |||
| }, | |||
| [setPagination, throttledGetDocumentList, dispatch], | |||
| ); | |||
| return { handleInputChange }; | |||
| }; | |||
| export const useSetSelectedRecord = () => { | |||
| const [currentRecord, setCurrentRecord] = useState<IKnowledgeFile>( | |||
| {} as IKnowledgeFile, | |||
| ); | |||
| const setRecord = (record: IKnowledgeFile) => () => { | |||
| setCurrentRecord(record); | |||
| }; | |||
| return { currentRecord, setRecord }; | |||
| }; | |||
| export const useRenameDocument = (documentId: string) => { | |||
| const saveName = useSaveDocumentName(); | |||
| const { | |||
| visible: renameVisible, | |||
| hideModal: hideRenameModal, | |||
| showModal: showRenameModal, | |||
| } = useSetModalState(); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', ['document_rename']); | |||
| const onRenameOk = useCallback( | |||
| async (name: string) => { | |||
| const ret = await saveName(documentId, name); | |||
| if (ret === 0) { | |||
| hideRenameModal(); | |||
| } | |||
| }, | |||
| [hideRenameModal, saveName, documentId], | |||
| ); | |||
| return { | |||
| renameLoading: loading, | |||
| onRenameOk, | |||
| renameVisible, | |||
| hideRenameModal, | |||
| showRenameModal, | |||
| }; | |||
| }; | |||
| export const useCreateEmptyDocument = () => { | |||
| const createDocument = useCreateDocument(); | |||
| const { | |||
| visible: createVisible, | |||
| hideModal: hideCreateModal, | |||
| showModal: showCreateModal, | |||
| } = useSetModalState(); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', ['document_create']); | |||
| const onCreateOk = useCallback( | |||
| async (name: string) => { | |||
| const ret = await createDocument(name); | |||
| if (ret === 0) { | |||
| hideCreateModal(); | |||
| } | |||
| }, | |||
| [hideCreateModal, createDocument], | |||
| ); | |||
| return { | |||
| createLoading: loading, | |||
| onCreateOk, | |||
| createVisible, | |||
| hideCreateModal, | |||
| showCreateModal, | |||
| }; | |||
| }; | |||
| export const useChangeDocumentParser = (documentId: string) => { | |||
| const setDocumentParser = useSetDocumentParser(); | |||
| const { | |||
| visible: changeParserVisible, | |||
| hideModal: hideChangeParserModal, | |||
| showModal: showChangeParserModal, | |||
| } = useSetModalState(); | |||
| const loading = useOneNamespaceEffectsLoading('kFModel', [ | |||
| 'document_change_parser', | |||
| ]); | |||
| const onChangeParserOk = useCallback( | |||
| async (parserId: string) => { | |||
| const ret = await setDocumentParser(parserId, documentId); | |||
| if (ret === 0) { | |||
| hideChangeParserModal(); | |||
| } | |||
| }, | |||
| [hideChangeParserModal, setDocumentParser, documentId], | |||
| ); | |||
| return { | |||
| changeParserLoading: loading, | |||
| onChangeParserOk, | |||
| changeParserVisible, | |||
| hideChangeParserModal, | |||
| showChangeParserModal, | |||
| }; | |||
| }; | |||
| @@ -1,12 +1,9 @@ | |||
| import { KnowledgeRouteKey } from '@/constants/knowledge'; | |||
| import { useKnowledgeBaseId } from '@/hooks/knowledgeHook'; | |||
| import { | |||
| useFetchTenantInfo, | |||
| useSelectParserList, | |||
| } from '@/hooks/userSettingHook'; | |||
| import { Pagination } from '@/interfaces/common'; | |||
| useSelectDocumentList, | |||
| useSetDocumentStatus, | |||
| } from '@/hooks/documentHooks'; | |||
| import { useSelectParserList } from '@/hooks/userSettingHook'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { getOneNamespaceEffectsLoading } from '@/utils/storeUtil'; | |||
| import { | |||
| FileOutlined, | |||
| FileTextOutlined, | |||
| @@ -25,133 +22,57 @@ import { | |||
| Tag, | |||
| } from 'antd'; | |||
| import type { ColumnsType } from 'antd/es/table'; | |||
| import { PaginationProps } from 'antd/lib'; | |||
| import React, { useCallback, useEffect, useMemo, useState } from 'react'; | |||
| import { useDispatch, useNavigate, useSelector } from 'umi'; | |||
| import CreateEPModal from './createEFileModal'; | |||
| import styles from './index.less'; | |||
| import { useMemo } from 'react'; | |||
| import ChunkMethodModal from './chunk-method-modal'; | |||
| import CreateFileModal from './create-file-modal'; | |||
| import { | |||
| useChangeDocumentParser, | |||
| useCreateEmptyDocument, | |||
| useFetchDocumentListOnMount, | |||
| useGetPagination, | |||
| useHandleSearchChange, | |||
| useNavigateToOtherPage, | |||
| useRenameDocument, | |||
| useSetSelectedRecord, | |||
| } from './hooks'; | |||
| import ParsingActionCell from './parsing-action-cell'; | |||
| import ParsingStatusCell from './parsing-status-cell'; | |||
| import RenameModal from './rename-modal'; | |||
| import SegmentSetModal from './segmentSetModal'; | |||
| const KnowledgeFile = () => { | |||
| const dispatch = useDispatch(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const effects = useSelector((state: any) => state.loading.effects); | |||
| const { data, total } = kFModel; | |||
| const knowledgeBaseId = useKnowledgeBaseId(); | |||
| import styles from './index.less'; | |||
| const loading = getOneNamespaceEffectsLoading('kFModel', effects, [ | |||
| 'getKfList', | |||
| 'updateDocumentStatus', | |||
| ]); | |||
| const [doc_id, setDocId] = useState('0'); | |||
| const [parser_id, setParserId] = useState('0'); | |||
| let navigate = useNavigate(); | |||
| const KnowledgeFile = () => { | |||
| const data = useSelectDocumentList(); | |||
| const { fetchDocumentList } = useFetchDocumentListOnMount(); | |||
| const parserList = useSelectParserList(); | |||
| const getKfList = useCallback(() => { | |||
| const payload = { | |||
| kb_id: knowledgeBaseId, | |||
| }; | |||
| dispatch({ | |||
| type: 'kFModel/getKfList', | |||
| payload, | |||
| }); | |||
| }, [dispatch, knowledgeBaseId]); | |||
| const throttledGetDocumentList = () => { | |||
| dispatch({ | |||
| type: 'kFModel/throttledGetDocumentList', | |||
| payload: knowledgeBaseId, | |||
| }); | |||
| }; | |||
| const setPagination = useCallback( | |||
| (pageNumber = 1, pageSize?: number) => { | |||
| const pagination: Pagination = { | |||
| current: pageNumber, | |||
| } as Pagination; | |||
| if (pageSize) { | |||
| pagination.pageSize = pageSize; | |||
| } | |||
| dispatch({ | |||
| type: 'kFModel/setPagination', | |||
| payload: pagination, | |||
| }); | |||
| }, | |||
| [dispatch], | |||
| ); | |||
| const onPageChange: PaginationProps['onChange'] = useCallback( | |||
| (pageNumber: number, pageSize: number) => { | |||
| setPagination(pageNumber, pageSize); | |||
| getKfList(); | |||
| }, | |||
| [getKfList, setPagination], | |||
| ); | |||
| const pagination: PaginationProps = useMemo(() => { | |||
| return { | |||
| showQuickJumper: true, | |||
| total, | |||
| showSizeChanger: true, | |||
| current: kFModel.pagination.currentPage, | |||
| pageSize: kFModel.pagination.pageSize, | |||
| pageSizeOptions: [1, 2, 10, 20, 50, 100], | |||
| onChange: onPageChange, | |||
| }; | |||
| }, [total, kFModel.pagination, onPageChange]); | |||
| useEffect(() => { | |||
| if (knowledgeBaseId) { | |||
| getKfList(); | |||
| dispatch({ | |||
| type: 'kFModel/pollGetDocumentList-start', | |||
| payload: knowledgeBaseId, | |||
| }); | |||
| } | |||
| return () => { | |||
| dispatch({ | |||
| type: 'kFModel/pollGetDocumentList-stop', | |||
| }); | |||
| }; | |||
| }, [knowledgeBaseId, dispatch, getKfList]); | |||
| const handleInputChange = ( | |||
| e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, | |||
| ) => { | |||
| const value = e.target.value; | |||
| dispatch({ type: 'kFModel/setSearchString', payload: value }); | |||
| setPagination(); | |||
| throttledGetDocumentList(); | |||
| }; | |||
| const onChangeStatus = (e: boolean, doc_id: string) => { | |||
| dispatch({ | |||
| type: 'kFModel/updateDocumentStatus', | |||
| payload: { | |||
| doc_id, | |||
| status: Number(e), | |||
| kb_id: knowledgeBaseId, | |||
| }, | |||
| }); | |||
| }; | |||
| const showCEFModal = useCallback(() => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowCEFwModal: true, | |||
| }, | |||
| }); | |||
| }, [dispatch]); | |||
| const linkToUploadPage = useCallback(() => { | |||
| navigate(`/knowledge/dataset/upload?id=${knowledgeBaseId}`); | |||
| }, [navigate, knowledgeBaseId]); | |||
| const { pagination, setPagination, total, searchString } = | |||
| useGetPagination(fetchDocumentList); | |||
| const onChangeStatus = useSetDocumentStatus(); | |||
| const { linkToUploadPage, toChunk } = useNavigateToOtherPage(); | |||
| const { handleInputChange } = useHandleSearchChange(setPagination); | |||
| const { currentRecord, setRecord } = useSetSelectedRecord(); | |||
| const { | |||
| renameLoading, | |||
| onRenameOk, | |||
| renameVisible, | |||
| hideRenameModal, | |||
| showRenameModal, | |||
| } = useRenameDocument(currentRecord.id); | |||
| const { | |||
| createLoading, | |||
| onCreateOk, | |||
| createVisible, | |||
| hideCreateModal, | |||
| showCreateModal, | |||
| } = useCreateEmptyDocument(); | |||
| const { | |||
| changeParserLoading, | |||
| onChangeParserOk, | |||
| changeParserVisible, | |||
| hideChangeParserModal, | |||
| showChangeParserModal, | |||
| } = useChangeDocumentParser(currentRecord.id); | |||
| const actionItems: MenuProps['items'] = useMemo(() => { | |||
| return [ | |||
| @@ -172,7 +93,7 @@ const KnowledgeFile = () => { | |||
| { type: 'divider' }, | |||
| { | |||
| key: '2', | |||
| onClick: showCEFModal, | |||
| onClick: showCreateModal, | |||
| label: ( | |||
| <div> | |||
| <Button type="link"> | |||
| @@ -184,18 +105,7 @@ const KnowledgeFile = () => { | |||
| // disabled: true, | |||
| }, | |||
| ]; | |||
| }, [linkToUploadPage, showCEFModal]); | |||
| const toChunk = (id: string) => { | |||
| navigate( | |||
| `/knowledge/${KnowledgeRouteKey.Dataset}/chunk?id=${knowledgeBaseId}&doc_id=${id}`, | |||
| ); | |||
| }; | |||
| const setDocumentAndParserId = (record: IKnowledgeFile) => () => { | |||
| setDocId(record.id); | |||
| setParserId(record.parser_id); | |||
| }; | |||
| }, [linkToUploadPage, showCreateModal]); | |||
| const columns: ColumnsType<IKnowledgeFile> = [ | |||
| { | |||
| @@ -255,8 +165,12 @@ const KnowledgeFile = () => { | |||
| key: 'action', | |||
| render: (_, record) => ( | |||
| <ParsingActionCell | |||
| knowledgeBaseId={knowledgeBaseId} | |||
| setDocumentAndParserId={setDocumentAndParserId(record)} | |||
| setDocumentAndParserId={setRecord(record)} | |||
| showRenameModal={() => { | |||
| setRecord(record)(); | |||
| showRenameModal(); | |||
| }} | |||
| showChangeParserModal={showChangeParserModal} | |||
| record={record} | |||
| ></ParsingActionCell> | |||
| ), | |||
| @@ -268,8 +182,6 @@ const KnowledgeFile = () => { | |||
| className: `${styles.column}`, | |||
| })); | |||
| useFetchTenantInfo(); | |||
| return ( | |||
| <div className={styles.datasetWrapper}> | |||
| <h3>Dataset</h3> | |||
| @@ -283,7 +195,7 @@ const KnowledgeFile = () => { | |||
| <Space> | |||
| <Input | |||
| placeholder="Seach your files" | |||
| value={kFModel.searchString} | |||
| value={searchString} | |||
| style={{ width: 220 }} | |||
| allowClear | |||
| onChange={handleInputChange} | |||
| @@ -305,13 +217,26 @@ const KnowledgeFile = () => { | |||
| pagination={pagination} | |||
| scroll={{ scrollToFirstRowOnChange: true, x: 1300, y: 'fill' }} | |||
| /> | |||
| <CreateEPModal getKfList={getKfList} kb_id={knowledgeBaseId} /> | |||
| <SegmentSetModal | |||
| getKfList={getKfList} | |||
| parser_id={parser_id} | |||
| doc_id={doc_id} | |||
| <CreateFileModal | |||
| visible={createVisible} | |||
| hideModal={hideCreateModal} | |||
| loading={createLoading} | |||
| onOk={onCreateOk} | |||
| /> | |||
| <ChunkMethodModal | |||
| parser_id={currentRecord.parser_id} | |||
| onOk={onChangeParserOk} | |||
| visible={changeParserVisible} | |||
| hideModal={hideChangeParserModal} | |||
| loading={changeParserLoading} | |||
| /> | |||
| <RenameModal></RenameModal> | |||
| <RenameModal | |||
| visible={renameVisible} | |||
| onOk={onRenameOk} | |||
| loading={renameLoading} | |||
| hideModal={hideRenameModal} | |||
| initialName={currentRecord.name} | |||
| ></RenameModal> | |||
| </div> | |||
| ); | |||
| }; | |||
| @@ -164,6 +164,10 @@ const model: DvaModel<KFModelState> = { | |||
| const { data } = yield call(kbService.document_create, payload); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| put({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| @@ -192,9 +196,16 @@ const model: DvaModel<KFModelState> = { | |||
| return retcode; | |||
| }, | |||
| *document_change_parser({ payload = {} }, { call, put }) { | |||
| const { data } = yield call(kbService.document_change_parser, payload); | |||
| const { data } = yield call( | |||
| kbService.document_change_parser, | |||
| omit(payload, ['kb_id']), | |||
| ); | |||
| const { retcode } = data; | |||
| if (retcode === 0) { | |||
| put({ | |||
| type: 'getKfList', | |||
| payload: { kb_id: payload.kb_id }, | |||
| }); | |||
| put({ | |||
| type: 'updateState', | |||
| payload: { | |||
| @@ -1,5 +1,8 @@ | |||
| import showDeleteConfirm from '@/components/deleting-confirm'; | |||
| import { useRemoveDocument } from '@/hooks/documentHooks'; | |||
| import { IKnowledgeFile } from '@/interfaces/database/knowledge'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { downloadFile } from '@/utils/fileUtil'; | |||
| import { | |||
| DeleteOutlined, | |||
| DownloadOutlined, | |||
| @@ -7,37 +10,27 @@ import { | |||
| ToolOutlined, | |||
| } from '@ant-design/icons'; | |||
| import { Button, Dropdown, MenuProps, Space, Tooltip } from 'antd'; | |||
| import { useDispatch } from 'umi'; | |||
| import { isParserRunning } from '../utils'; | |||
| import { api_host } from '@/utils/api'; | |||
| import { downloadFile } from '@/utils/fileUtil'; | |||
| import styles from './index.less'; | |||
| interface IProps { | |||
| knowledgeBaseId: string; | |||
| record: IKnowledgeFile; | |||
| setDocumentAndParserId: () => void; | |||
| showRenameModal: () => void; | |||
| showChangeParserModal: () => void; | |||
| } | |||
| const ParsingActionCell = ({ | |||
| knowledgeBaseId, | |||
| record, | |||
| setDocumentAndParserId, | |||
| showRenameModal, | |||
| showChangeParserModal, | |||
| }: IProps) => { | |||
| const dispatch = useDispatch(); | |||
| const documentId = record.id; | |||
| const isRunning = isParserRunning(record.run); | |||
| const removeDocument = () => { | |||
| dispatch({ | |||
| type: 'kFModel/document_rm', | |||
| payload: { | |||
| doc_id: documentId, | |||
| kb_id: knowledgeBaseId, | |||
| }, | |||
| }); | |||
| }; | |||
| const removeDocument = useRemoveDocument(documentId); | |||
| const onRmDocument = () => { | |||
| if (!isRunning) { | |||
| @@ -52,39 +45,13 @@ const ParsingActionCell = ({ | |||
| }); | |||
| }; | |||
| const setCurrentRecord = () => { | |||
| dispatch({ | |||
| type: 'kFModel/setCurrentRecord', | |||
| payload: record, | |||
| }); | |||
| }; | |||
| const showSegmentSetModal = () => { | |||
| dispatch({ | |||
| type: 'kFModel/updateState', | |||
| payload: { | |||
| isShowSegmentSetModal: true, | |||
| }, | |||
| }); | |||
| }; | |||
| const showRenameModal = () => { | |||
| if (!isRunning) { | |||
| setCurrentRecord(); | |||
| dispatch({ | |||
| type: 'kFModel/setIsShowRenameModal', | |||
| payload: true, | |||
| }); | |||
| } | |||
| }; | |||
| const chunkItems: MenuProps['items'] = [ | |||
| { | |||
| key: '1', | |||
| label: ( | |||
| <div> | |||
| <Button type="link" onClick={showSegmentSetModal}> | |||
| Category | |||
| <Button type="link" onClick={showChangeParserModal}> | |||
| Chunk Method | |||
| </Button> | |||
| </div> | |||
| ), | |||
| @@ -1,46 +1,30 @@ | |||
| import { useKnowledgeBaseId } from '@/hooks/knowledgeHook'; | |||
| import { IModalManagerChildrenProps } from '@/components/modal-manager'; | |||
| import { Form, Input, Modal } from 'antd'; | |||
| import { useEffect } from 'react'; | |||
| import { useDispatch, useSelector } from 'umi'; | |||
| const RenameModal = () => { | |||
| interface IProps extends Omit<IModalManagerChildrenProps, 'showModal'> { | |||
| loading: boolean; | |||
| initialName: string; | |||
| onOk: (name: string) => void; | |||
| showModal?(): void; | |||
| } | |||
| const RenameModal = ({ | |||
| visible, | |||
| onOk, | |||
| loading, | |||
| initialName, | |||
| hideModal, | |||
| }: IProps) => { | |||
| const [form] = Form.useForm(); | |||
| const dispatch = useDispatch(); | |||
| const kFModel = useSelector((state: any) => state.kFModel); | |||
| const loading = useSelector( | |||
| (state: any) => state.loading.effects['kFModel/document_rename'], | |||
| ); | |||
| const knowledgeBaseId = useKnowledgeBaseId(); | |||
| const isModalOpen = kFModel.isShowRenameModal; | |||
| const initialName = kFModel.currentRecord?.name; | |||
| const documentId = kFModel.currentRecord?.id; | |||
| type FieldType = { | |||
| name?: string; | |||
| }; | |||
| const closeModal = () => { | |||
| dispatch({ | |||
| type: 'kFModel/setIsShowRenameModal', | |||
| payload: false, | |||
| }); | |||
| }; | |||
| const handleOk = async () => { | |||
| const ret = await form.validateFields(); | |||
| dispatch({ | |||
| type: 'kFModel/document_rename', | |||
| payload: { | |||
| doc_id: documentId, | |||
| name: ret.name, | |||
| kb_id: knowledgeBaseId, | |||
| }, | |||
| }); | |||
| }; | |||
| const handleCancel = () => { | |||
| closeModal(); | |||
| onOk(ret.name); | |||
| }; | |||
| const onFinish = (values: any) => { | |||
| @@ -52,17 +36,17 @@ const RenameModal = () => { | |||
| }; | |||
| useEffect(() => { | |||
| if (isModalOpen) { | |||
| if (visible) { | |||
| form.setFieldValue('name', initialName); | |||
| } | |||
| }, [initialName, documentId, form, isModalOpen]); | |||
| }, [initialName, form, visible]); | |||
| return ( | |||
| <Modal | |||
| title="Rename" | |||
| open={isModalOpen} | |||
| open={visible} | |||
| onOk={handleOk} | |||
| onCancel={handleCancel} | |||
| onCancel={hideModal} | |||
| okButtonProps={{ loading }} | |||
| > | |||
| <Form | |||
| @@ -35,7 +35,11 @@ const CategoryPanel = ({ chunkMethod }: { chunkMethod: string }) => { | |||
| <Title level={5} className={styles.topTitle}> | |||
| {item.title} Category | |||
| </Title> | |||
| <Text>{item.description}</Text> | |||
| <p | |||
| dangerouslySetInnerHTML={{ | |||
| __html: item.description, | |||
| }} | |||
| ></p> | |||
| <Title level={5}>{item.title} Image Examples</Title> | |||
| <Text> | |||
| We've prepared detailed visual guides to make understanding easier | |||
| @@ -21,10 +21,10 @@ const Configuration = () => { | |||
| <Divider></Divider> | |||
| <Spin spinning={loading}> | |||
| <Row gutter={32}> | |||
| <Col span={12}> | |||
| <Col span={8}> | |||
| <ConfigurationForm form={form}></ConfigurationForm> | |||
| </Col> | |||
| <Col span={12}> | |||
| <Col span={16}> | |||
| <CategoryPanel chunkMethod={chunkMethod}></CategoryPanel> | |||
| </Col> | |||
| </Row> | |||
| @@ -1,4 +1,5 @@ | |||
| import { ReactComponent as ChatAppCube } from '@/assets/svg/chat-app-cube.svg'; | |||
| import RenameModal from '@/components/rename-modal'; | |||
| import { DeleteOutlined, EditOutlined, FormOutlined } from '@ant-design/icons'; | |||
| import { | |||
| Avatar, | |||
| @@ -34,7 +35,6 @@ import { | |||
| useSelectFirstDialogOnMount, | |||
| } from './hooks'; | |||
| import RenameModal from '@/components/rename-modal'; | |||
| import styles from './index.less'; | |||
| const Chat = () => { | |||