balibabu
feat: Display mindmap in drawer #2247 (#2430)
0dc6759
raw
history blame
9.36 kB
import FileIcon from '@/components/file-icon';
import HightLightMarkdown from '@/components/highlight-markdown';
import { ImageWithPopover } from '@/components/image';
import PdfDrawer from '@/components/pdf-drawer';
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
import RetrievalDocuments from '@/components/retrieval-documents';
import SvgIcon from '@/components/svg-icon';
import {
useNextFetchKnowledgeList,
useSelectTestingResult,
} from '@/hooks/knowledge-hooks';
import { useGetPaginationWithRouter } from '@/hooks/logic-hooks';
import { IReference } from '@/interfaces/database/chat';
import {
Card,
Divider,
Flex,
FloatButton,
Input,
Layout,
List,
Pagination,
PaginationProps,
Popover,
Skeleton,
Space,
Spin,
Tag,
Tooltip,
} from 'antd';
import DOMPurify from 'dompurify';
import { isEmpty } from 'lodash';
import { useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import MarkdownContent from '../chat/markdown-content';
import { useSendQuestion, useShowMindMapDrawer } from './hooks';
import styles from './index.less';
import MindMapDrawer from './mindmap-drawer';
import SearchSidebar from './sidebar';
const { Content } = Layout;
const { Search } = Input;
const SearchPage = () => {
const { t } = useTranslation();
const [checkedList, setCheckedList] = useState<string[]>([]);
const { chunks, total } = useSelectTestingResult();
const { list: knowledgeList } = useNextFetchKnowledgeList();
const checkedWithoutEmbeddingIdList = useMemo(() => {
return checkedList.filter((x) => knowledgeList.some((y) => y.id === x));
}, [checkedList, knowledgeList]);
const {
sendQuestion,
handleClickRelatedQuestion,
handleSearchStrChange,
handleTestChunk,
setSelectedDocumentIds,
answer,
sendingLoading,
relatedQuestions,
searchStr,
loading,
isFirstRender,
selectedDocumentIds,
isSearchStrEmpty,
} = useSendQuestion(checkedWithoutEmbeddingIdList);
const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
useClickDrawer();
const { pagination } = useGetPaginationWithRouter();
const {
mindMapVisible,
hideMindMapModal,
showMindMapModal,
mindMapLoading,
mindMap,
} = useShowMindMapDrawer(checkedWithoutEmbeddingIdList, searchStr);
const onChange: PaginationProps['onChange'] = (pageNumber, pageSize) => {
pagination.onChange?.(pageNumber, pageSize);
handleTestChunk(selectedDocumentIds, pageNumber, pageSize);
};
const InputSearch = (
<Search
value={searchStr}
onChange={handleSearchStrChange}
placeholder={t('header.search')}
allowClear
enterButton
onSearch={sendQuestion}
size="large"
loading={sendingLoading}
disabled={checkedWithoutEmbeddingIdList.length === 0}
className={isFirstRender ? styles.globalInput : styles.partialInput}
/>
);
return (
<>
<Layout className={styles.searchPage}>
<SearchSidebar
isFirstRender={isFirstRender}
checkedList={checkedWithoutEmbeddingIdList}
setCheckedList={setCheckedList}
></SearchSidebar>
<Layout className={isFirstRender ? styles.mainLayout : ''}>
<Content>
{isFirstRender ? (
<Flex justify="center" className={styles.firstRenderContent}>
<Flex vertical align="center" gap={'large'}>
{InputSearch}
</Flex>
</Flex>
) : (
<Flex className={styles.content}>
<section className={styles.main}>
{InputSearch}
<Card
title={
<Flex gap={10}>
<img src="/logo.svg" alt="" width={20} />
{t('chat.answerTitle')}
</Flex>
}
className={styles.answerWrapper}
>
{isEmpty(answer) && sendingLoading ? (
<Skeleton active />
) : (
answer.answer && (
<MarkdownContent
loading={sendingLoading}
content={answer.answer}
reference={answer.reference ?? ({} as IReference)}
clickDocumentButton={clickDocumentButton}
></MarkdownContent>
)
)}
</Card>
<Divider></Divider>
<RetrievalDocuments
selectedDocumentIds={selectedDocumentIds}
setSelectedDocumentIds={setSelectedDocumentIds}
onTesting={handleTestChunk}
></RetrievalDocuments>
<Divider></Divider>
<Spin spinning={loading}>
{chunks.length > 0 && (
<List
dataSource={chunks}
className={styles.chunks}
renderItem={(item) => (
<List.Item>
<Card className={styles.card}>
<Space>
<ImageWithPopover
id={item.img_id}
></ImageWithPopover>
<Flex vertical gap={10}>
<Popover
content={
<div className={styles.popupMarkdown}>
<HightLightMarkdown>
{item.content_with_weight}
</HightLightMarkdown>
</div>
}
>
<div
dangerouslySetInnerHTML={{
__html: DOMPurify.sanitize(
`${item.highlight}...`,
),
}}
className={styles.highlightContent}
></div>
</Popover>
<Space
className={styles.documentReference}
onClick={() =>
clickDocumentButton(
item.doc_id,
item as any,
)
}
>
<FileIcon
id={item.img_id}
name={item.docnm_kwd}
></FileIcon>
{item.docnm_kwd}
</Space>
</Flex>
</Space>
</Card>
</List.Item>
)}
/>
)}
</Spin>
{relatedQuestions?.length > 0 && (
<Card title={t('chat.relatedQuestion')}>
<Flex wrap="wrap" gap={'10px 0'}>
{relatedQuestions?.map((x, idx) => (
<Tag
key={idx}
className={styles.tag}
onClick={handleClickRelatedQuestion(x)}
>
{x}
</Tag>
))}
</Flex>
</Card>
)}
<Divider></Divider>
<Pagination
{...pagination}
total={total}
onChange={onChange}
className={styles.pagination}
/>
</section>
</Flex>
)}
</Content>
</Layout>
</Layout>
{!isFirstRender &&
!isSearchStrEmpty &&
!isEmpty(checkedWithoutEmbeddingIdList) && (
<Tooltip title={t('chunk.mind')} zIndex={1}>
<FloatButton
className={styles.mindMapFloatButton}
onClick={showMindMapModal}
icon={
<SvgIcon name="paper-clip" width={24} height={30}></SvgIcon>
}
/>
</Tooltip>
)}
{visible && (
<PdfDrawer
visible={visible}
hideModal={hideModal}
documentId={documentId}
chunk={selectedChunk}
></PdfDrawer>
)}
{mindMapVisible && (
<MindMapDrawer
visible={mindMapVisible}
hideModal={hideMindMapModal}
data={mindMap}
loading={mindMapLoading}
></MindMapDrawer>
)}
</>
);
};
export default SearchPage;