balibabu commited on
Commit
fa5e9f6
·
1 Parent(s): e1017ef

feat: Add RetrievalDocuments to SearchPage #2247 (#2327)

Browse files

### What problem does this PR solve?
feat: Add RetrievalDocuments to SearchPage #2247
feat: Click on the link in the reference to display the pdf drawer #2247

### Type of change


- [x] New Feature (non-breaking change which adds functionality)

web/src/components/pdf-drawer/hooks.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useSetModalState } from '@/hooks/common-hooks';
2
+ import { IChunk } from '@/interfaces/database/knowledge';
3
+ import { useCallback, useState } from 'react';
4
+
5
+ export const useClickDrawer = () => {
6
+ const { visible, showModal, hideModal } = useSetModalState();
7
+ const [selectedChunk, setSelectedChunk] = useState<IChunk>({} as IChunk);
8
+ const [documentId, setDocumentId] = useState<string>('');
9
+
10
+ const clickDocumentButton = useCallback(
11
+ (documentId: string, chunk: IChunk) => {
12
+ showModal();
13
+ setSelectedChunk(chunk);
14
+ setDocumentId(documentId);
15
+ },
16
+ [showModal],
17
+ );
18
+
19
+ return {
20
+ clickDocumentButton,
21
+ visible,
22
+ showModal,
23
+ hideModal,
24
+ selectedChunk,
25
+ documentId,
26
+ };
27
+ };
web/src/components/pdf-drawer/index.tsx ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { IModalProps } from '@/interfaces/common';
2
+ import { IChunk } from '@/interfaces/database/knowledge';
3
+ import { Drawer } from 'antd';
4
+ import DocumentPreviewer from '../pdf-previewer';
5
+
6
+ interface IProps extends IModalProps<any> {
7
+ documentId: string;
8
+ chunk: IChunk;
9
+ }
10
+
11
+ export const PdfDrawer = ({
12
+ visible = false,
13
+ hideModal,
14
+ documentId,
15
+ chunk,
16
+ }: IProps) => {
17
+ return (
18
+ <Drawer
19
+ title="Document Previewer"
20
+ onClose={hideModal}
21
+ open={visible}
22
+ width={'50vw'}
23
+ >
24
+ <DocumentPreviewer
25
+ documentId={documentId}
26
+ chunk={chunk}
27
+ visible={visible}
28
+ ></DocumentPreviewer>
29
+ </Drawer>
30
+ );
31
+ };
32
+
33
+ export default PdfDrawer;
web/src/components/retrieval-documents/index.less ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .selectFilesCollapse {
2
+ :global(.ant-collapse-header) {
3
+ padding-left: 22px;
4
+ }
5
+ margin-bottom: 32px;
6
+ overflow-y: auto;
7
+ }
8
+
9
+ .selectFilesTitle {
10
+ padding-right: 10px;
11
+ }
web/src/components/retrieval-documents/index.tsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ReactComponent as SelectedFilesCollapseIcon } from '@/assets/svg/selected-files-collapse.svg';
2
+ import { Collapse, Flex, Space } from 'antd';
3
+ import SelectFiles from './select-files';
4
+
5
+ import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
6
+ import { useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import styles from './index.less';
9
+
10
+ interface IProps {
11
+ selectedDocumentIdsLength?: number;
12
+ onTesting(documentIds: string[]): void;
13
+ }
14
+
15
+ const RetrievalDocuments = ({ onTesting }: IProps) => {
16
+ const { t } = useTranslation();
17
+ const { documents } = useSelectTestingResult();
18
+ const [selectedDocumentIds, setSelectedDocumentIds] = useState<string[]>([]);
19
+
20
+ return (
21
+ <Collapse
22
+ expandIcon={() => <SelectedFilesCollapseIcon></SelectedFilesCollapseIcon>}
23
+ className={styles.selectFilesCollapse}
24
+ items={[
25
+ {
26
+ key: '1',
27
+ label: (
28
+ <Flex
29
+ justify={'space-between'}
30
+ align="center"
31
+ className={styles.selectFilesTitle}
32
+ >
33
+ <Space>
34
+ <span>
35
+ {selectedDocumentIds.length ?? 0}/{documents.length}
36
+ </span>
37
+ {t('knowledgeDetails.filesSelected')}
38
+ </Space>
39
+ </Flex>
40
+ ),
41
+ children: (
42
+ <div>
43
+ <SelectFiles
44
+ setSelectedDocumentIds={setSelectedDocumentIds}
45
+ handleTesting={onTesting}
46
+ ></SelectFiles>
47
+ </div>
48
+ ),
49
+ },
50
+ ]}
51
+ />
52
+ );
53
+ };
54
+
55
+ export default RetrievalDocuments;
web/src/components/retrieval-documents/select-files.tsx ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import NewDocumentLink from '@/components/new-document-link';
2
+ import { useTranslate } from '@/hooks/common-hooks';
3
+ import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
4
+ import { ITestingDocument } from '@/interfaces/database/knowledge';
5
+ import { EyeOutlined } from '@ant-design/icons';
6
+ import { Button, Table, TableProps, Tooltip } from 'antd';
7
+
8
+ interface IProps {
9
+ handleTesting: (ids: string[]) => void;
10
+ setSelectedDocumentIds: (ids: string[]) => void;
11
+ }
12
+
13
+ const SelectFiles = ({ setSelectedDocumentIds, handleTesting }: IProps) => {
14
+ const { documents } = useSelectTestingResult();
15
+ const { t } = useTranslate('fileManager');
16
+
17
+ const columns: TableProps<ITestingDocument>['columns'] = [
18
+ {
19
+ title: 'Name',
20
+ dataIndex: 'doc_name',
21
+ key: 'doc_name',
22
+ render: (text) => <p>{text}</p>,
23
+ },
24
+
25
+ {
26
+ title: 'Hits',
27
+ dataIndex: 'count',
28
+ key: 'count',
29
+ width: 80,
30
+ },
31
+ {
32
+ title: 'View',
33
+ key: 'view',
34
+ width: 50,
35
+ render: (_, { doc_id, doc_name }) => (
36
+ <NewDocumentLink
37
+ documentName={doc_name}
38
+ documentId={doc_id}
39
+ prefix="document"
40
+ >
41
+ <Tooltip title={t('preview')}>
42
+ <Button type="text">
43
+ <EyeOutlined size={20} />
44
+ </Button>
45
+ </Tooltip>
46
+ </NewDocumentLink>
47
+ ),
48
+ },
49
+ ];
50
+
51
+ const rowSelection = {
52
+ onChange: (selectedRowKeys: React.Key[]) => {
53
+ handleTesting(selectedRowKeys as string[]);
54
+ setSelectedDocumentIds(selectedRowKeys as string[]);
55
+ },
56
+ getCheckboxProps: (record: ITestingDocument) => ({
57
+ disabled: record.doc_name === 'Disabled User', // Column configuration not to be checked
58
+ name: record.doc_name,
59
+ }),
60
+ };
61
+
62
+ return (
63
+ <Table
64
+ columns={columns}
65
+ dataSource={documents}
66
+ showHeader={false}
67
+ rowSelection={rowSelection}
68
+ rowKey={'doc_id'}
69
+ />
70
+ );
71
+ };
72
+
73
+ export default SelectFiles;
web/src/locales/en.ts CHANGED
@@ -646,7 +646,7 @@ The above is the content you need to summarize.`,
646
  operation: 'operation',
647
  run: 'Run',
648
  save: 'Save',
649
- title: 'Title:',
650
  beginDescription: 'This is where the flow begins.',
651
  answerDescription: `A component that serves as the interface between human and bot, receiving user inputs and displaying the agent's responses.`,
652
  retrievalDescription: `A component that retrieves information from a specified knowledge base and returns 'Empty response' if no information is found. Ensure the correct knowledge base is selected.`,
 
646
  operation: 'operation',
647
  run: 'Run',
648
  save: 'Save',
649
+ title: 'ID:',
650
  beginDescription: 'This is where the flow begins.',
651
  answerDescription: `A component that serves as the interface between human and bot, receiving user inputs and displaying the agent's responses.`,
652
  retrievalDescription: `A component that retrieves information from a specified knowledge base and returns 'Empty response' if no information is found. Ensure the correct knowledge base is selected.`,
web/src/locales/zh-traditional.ts CHANGED
@@ -602,7 +602,7 @@ export default {
602
  operation: '操作',
603
  run: '運行',
604
  save: '儲存',
605
- title: '標題:',
606
 
607
  beginDescription: '這是流程開始的地方',
608
  answerDescription: `該組件用作機器人與人類之間的介面。它接收使用者的輸入並顯示機器人的計算結果。`,
 
602
  operation: '操作',
603
  run: '運行',
604
  save: '儲存',
605
+ title: 'ID:',
606
 
607
  beginDescription: '這是流程開始的地方',
608
  answerDescription: `該組件用作機器人與人類之間的介面。它接收使用者的輸入並顯示機器人的計算結果。`,
web/src/locales/zh.ts CHANGED
@@ -621,7 +621,7 @@ export default {
621
  operation: '操作',
622
  run: '运行',
623
  save: '保存',
624
- title: '标题:',
625
  beginDescription: '这是流程开始的地方',
626
  answerDescription: `该组件用作机器人与人类之间的接口。它接收用户的输入并显示机器人的计算结果。`,
627
  retrievalDescription: `此组件用于从知识库中检索相关信息。选择知识库。如果没有检索到任何内容,将返回“空响应”。`,
 
621
  operation: '操作',
622
  run: '运行',
623
  save: '保存',
624
+ title: 'ID:',
625
  beginDescription: '这是流程开始的地方',
626
  answerDescription: `该组件用作机器人与人类之间的接口。它接收用户的输入并显示机器人的计算结果。`,
627
  retrievalDescription: `此组件用于从知识库中检索相关信息。选择知识库。如果没有检索到任何内容,将返回“空响应”。`,
web/src/pages/chat/chat-container/index.tsx CHANGED
@@ -1,9 +1,7 @@
1
  import MessageItem from '@/components/message-item';
2
- import DocumentPreviewer from '@/components/pdf-previewer';
3
  import { MessageType } from '@/constants/chat';
4
- import { Drawer, Flex, Spin } from 'antd';
5
  import {
6
- useClickDrawer,
7
  useCreateConversationBeforeUploadDocument,
8
  useGetFileIcon,
9
  useGetSendButtonDisabled,
@@ -13,6 +11,8 @@ import {
13
  import { buildMessageItemReference } from '../utils';
14
 
15
  import MessageInput from '@/components/message-input';
 
 
16
  import {
17
  useFetchNextConversation,
18
  useGetChatSearchParams,
@@ -96,18 +96,12 @@ const ChatContainer = () => {
96
  }
97
  ></MessageInput>
98
  </Flex>
99
- <Drawer
100
- title="Document Previewer"
101
- onClose={hideModal}
102
- open={visible}
103
- width={'50vw'}
104
- >
105
- <DocumentPreviewer
106
- documentId={documentId}
107
- chunk={selectedChunk}
108
- visible={visible}
109
- ></DocumentPreviewer>
110
- </Drawer>
111
  </>
112
  );
113
  };
 
1
  import MessageItem from '@/components/message-item';
 
2
  import { MessageType } from '@/constants/chat';
3
+ import { Flex, Spin } from 'antd';
4
  import {
 
5
  useCreateConversationBeforeUploadDocument,
6
  useGetFileIcon,
7
  useGetSendButtonDisabled,
 
11
  import { buildMessageItemReference } from '../utils';
12
 
13
  import MessageInput from '@/components/message-input';
14
+ import PdfDrawer from '@/components/pdf-drawer';
15
+ import { useClickDrawer } from '@/components/pdf-drawer/hooks';
16
  import {
17
  useFetchNextConversation,
18
  useGetChatSearchParams,
 
96
  }
97
  ></MessageInput>
98
  </Flex>
99
+ <PdfDrawer
100
+ visible={visible}
101
+ hideModal={hideModal}
102
+ documentId={documentId}
103
+ chunk={selectedChunk}
104
+ ></PdfDrawer>
 
 
 
 
 
 
105
  </>
106
  );
107
  };
web/src/pages/chat/hooks.ts CHANGED
@@ -23,7 +23,6 @@ import {
23
  useSendMessageWithSse,
24
  } from '@/hooks/logic-hooks';
25
  import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
26
- import { IChunk } from '@/interfaces/database/knowledge';
27
  import { getFileExtension } from '@/utils';
28
  import { useMutationState } from '@tanstack/react-query';
29
  import { get } from 'lodash';
@@ -545,30 +544,6 @@ export const useRenameConversation = () => {
545
  };
546
  };
547
 
548
- export const useClickDrawer = () => {
549
- const { visible, showModal, hideModal } = useSetModalState();
550
- const [selectedChunk, setSelectedChunk] = useState<IChunk>({} as IChunk);
551
- const [documentId, setDocumentId] = useState<string>('');
552
-
553
- const clickDocumentButton = useCallback(
554
- (documentId: string, chunk: IChunk) => {
555
- showModal();
556
- setSelectedChunk(chunk);
557
- setDocumentId(documentId);
558
- },
559
- [showModal],
560
- );
561
-
562
- return {
563
- clickDocumentButton,
564
- visible,
565
- showModal,
566
- hideModal,
567
- selectedChunk,
568
- documentId,
569
- };
570
- };
571
-
572
  export const useGetSendButtonDisabled = () => {
573
  const { dialogId, conversationId } = useGetChatSearchParams();
574
 
 
23
  useSendMessageWithSse,
24
  } from '@/hooks/logic-hooks';
25
  import { IConversation, IDialog, Message } from '@/interfaces/database/chat';
 
26
  import { getFileExtension } from '@/utils';
27
  import { useMutationState } from '@tanstack/react-query';
28
  import { get } from 'lodash';
 
544
  };
545
  };
546
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
  export const useGetSendButtonDisabled = () => {
548
  const { dialogId, conversationId } = useGetChatSearchParams();
549
 
web/src/pages/flow/chat/box.tsx CHANGED
@@ -1,13 +1,14 @@
1
  import MessageItem from '@/components/message-item';
2
- import DocumentPreviewer from '@/components/pdf-previewer';
3
  import { MessageType } from '@/constants/chat';
4
  import { useTranslate } from '@/hooks/common-hooks';
5
- import { useClickDrawer, useGetFileIcon } from '@/pages/chat/hooks';
6
  import { buildMessageItemReference } from '@/pages/chat/utils';
7
- import { Button, Drawer, Flex, Input, Spin } from 'antd';
8
 
9
  import { useSendNextMessage } from './hooks';
10
 
 
 
11
  import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
12
  import styles from './index.less';
13
 
@@ -79,19 +80,12 @@ const FlowChatBox = () => {
79
  onChange={handleInputChange}
80
  />
81
  </Flex>
82
- <Drawer
83
- title="Document Previewer"
84
- onClose={hideModal}
85
- open={visible}
86
- width={'50vw'}
87
- mask={false}
88
- >
89
- <DocumentPreviewer
90
- documentId={documentId}
91
- chunk={selectedChunk}
92
- visible={visible}
93
- ></DocumentPreviewer>
94
- </Drawer>
95
  </>
96
  );
97
  };
 
1
  import MessageItem from '@/components/message-item';
 
2
  import { MessageType } from '@/constants/chat';
3
  import { useTranslate } from '@/hooks/common-hooks';
4
+ import { useGetFileIcon } from '@/pages/chat/hooks';
5
  import { buildMessageItemReference } from '@/pages/chat/utils';
6
+ import { Button, Flex, Input, Spin } from 'antd';
7
 
8
  import { useSendNextMessage } from './hooks';
9
 
10
+ import PdfDrawer from '@/components/pdf-drawer';
11
+ import { useClickDrawer } from '@/components/pdf-drawer/hooks';
12
  import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
13
  import styles from './index.less';
14
 
 
80
  onChange={handleInputChange}
81
  />
82
  </Flex>
83
+ <PdfDrawer
84
+ visible={visible}
85
+ hideModal={hideModal}
86
+ documentId={documentId}
87
+ chunk={selectedChunk}
88
+ ></PdfDrawer>
 
 
 
 
 
 
 
89
  </>
90
  );
91
  };
web/src/pages/search/hooks.ts CHANGED
@@ -46,10 +46,27 @@ export const useSendQuestion = (kbIds: string[]) => {
46
 
47
  const handleClickRelatedQuestion = useCallback(
48
  (question: string) => () => {
 
 
49
  setSearchStr(question);
50
  sendQuestion(question);
51
  },
52
- [sendQuestion],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  );
54
 
55
  useEffect(() => {
@@ -71,6 +88,7 @@ export const useSendQuestion = (kbIds: string[]) => {
71
  sendQuestion,
72
  handleSearchStrChange,
73
  handleClickRelatedQuestion,
 
74
  loading,
75
  sendingLoading,
76
  answer: currentAnswer,
 
46
 
47
  const handleClickRelatedQuestion = useCallback(
48
  (question: string) => () => {
49
+ if (sendingLoading) return;
50
+
51
  setSearchStr(question);
52
  sendQuestion(question);
53
  },
54
+ [sendQuestion, sendingLoading],
55
+ );
56
+
57
+ const handleTestChunk = useCallback(
58
+ (documentIds: string[]) => {
59
+ const q = trim(searchStr);
60
+ if (sendingLoading || isEmpty(q)) return;
61
+
62
+ testChunk({
63
+ kb_id: kbIds,
64
+ highlight: true,
65
+ question: q,
66
+ doc_ids: Array.isArray(documentIds) ? documentIds : [],
67
+ });
68
+ },
69
+ [sendingLoading, searchStr, kbIds, testChunk],
70
  );
71
 
72
  useEffect(() => {
 
88
  sendQuestion,
89
  handleSearchStrChange,
90
  handleClickRelatedQuestion,
91
+ handleTestChunk,
92
  loading,
93
  sendingLoading,
94
  answer: currentAnswer,
web/src/pages/search/index.less CHANGED
@@ -51,6 +51,9 @@
51
 
52
  .firstRenderContent {
53
  height: 100%;
 
 
 
54
  }
55
 
56
  .content {
@@ -79,10 +82,13 @@
79
 
80
  .input() {
81
  :global(.ant-input-affix-wrapper) {
82
- padding: 4px 8px;
83
  border-start-start-radius: 30px !important;
84
  border-end-start-radius: 30px !important;
85
  }
 
 
 
86
  input {
87
  height: 40px;
88
  }
@@ -101,3 +107,35 @@
101
  width: 100%;
102
  .input();
103
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
  .firstRenderContent {
53
  height: 100%;
54
+ background-image: url(https://www.bing.com/th?id=OHR.IguazuRainbow_ZH-CN6524347982_1920x1080.webp&qlt=50);
55
+ background-position: center;
56
+ background-size: cover;
57
  }
58
 
59
  .content {
 
82
 
83
  .input() {
84
  :global(.ant-input-affix-wrapper) {
85
+ padding: 4px 12px;
86
  border-start-start-radius: 30px !important;
87
  border-end-start-radius: 30px !important;
88
  }
89
+ :global(.ant-input-group-addon) {
90
+ background-color: transparent;
91
+ }
92
  input {
93
  height: 40px;
94
  }
 
107
  width: 100%;
108
  .input();
109
  }
110
+
111
+ .appIcon {
112
+ display: inline-block;
113
+ vertical-align: middle;
114
+ width: 60px;
115
+ }
116
+
117
+ .appName {
118
+ vertical-align: middle;
119
+ font-family: Inter;
120
+ font-size: 40px;
121
+ font-style: normal;
122
+ font-weight: 600;
123
+ line-height: 20px;
124
+
125
+ background: linear-gradient(to right, #095fab 10%, #25abe8 50%, #57d75b 60%);
126
+ background-size: auto auto;
127
+ background-clip: border-box;
128
+ background-size: 200% auto;
129
+ color: #fff;
130
+ background-clip: text;
131
+ text-fill-color: transparent;
132
+ -webkit-background-clip: text;
133
+ -webkit-text-fill-color: transparent;
134
+ animation: textclip 1.5s linear infinite;
135
+ }
136
+
137
+ @keyframes textclip {
138
+ to {
139
+ background-position: 200% center;
140
+ }
141
+ }
web/src/pages/search/index.tsx CHANGED
@@ -19,18 +19,26 @@ import MarkdownContent from '../chat/markdown-content';
19
  import { useSendQuestion } from './hooks';
20
  import SearchSidebar from './sidebar';
21
 
 
 
 
 
 
22
  import styles from './index.less';
23
 
24
  const { Content } = Layout;
25
  const { Search } = Input;
26
 
27
  const SearchPage = () => {
 
28
  const [checkedList, setCheckedList] = useState<string[]>([]);
29
  const list = useSelectTestingResult();
 
30
  const {
31
  sendQuestion,
32
  handleClickRelatedQuestion,
33
  handleSearchStrChange,
 
34
  answer,
35
  sendingLoading,
36
  relatedQuestions,
@@ -40,12 +48,14 @@ const SearchPage = () => {
40
  loading,
41
  isFirstRender,
42
  } = useSendQuestion(checkedList);
 
 
43
 
44
  const InputSearch = (
45
  <Search
46
  value={searchStr}
47
  onChange={handleSearchStrChange}
48
- placeholder="input search text"
49
  allowClear
50
  enterButton
51
  onSearch={sendQuestion}
@@ -57,88 +67,107 @@ const SearchPage = () => {
57
  );
58
 
59
  return (
60
- <Layout className={styles.searchPage}>
61
- <SearchSidebar
62
- checkedList={checkedList}
63
- setCheckedList={setCheckedList}
64
- ></SearchSidebar>
65
- <Layout>
66
- <Content>
67
- {isFirstRender ? (
68
- <Flex
69
- justify="center"
70
- align="center"
71
- className={styles.firstRenderContent}
72
- >
73
- {InputSearch}
74
- </Flex>
75
- ) : (
76
- <Flex className={styles.content}>
77
- <section className={styles.main}>
78
- {InputSearch}
79
- {answer.answer && (
80
- <div className={styles.answerWrapper}>
81
- <MarkdownContent
82
- loading={sendingLoading}
83
- content={answer.answer}
84
- reference={answer.reference ?? ({} as IReference)}
85
- clickDocumentButton={() => {}}
86
- ></MarkdownContent>
87
- </div>
88
- )}
89
- <Divider></Divider>
90
- {list.chunks.length > 0 && (
91
- <List
92
- dataSource={list.chunks}
93
- loading={loading}
94
- renderItem={(item) => (
95
- <List.Item>
96
- <Card className={styles.card}>
97
- <Space>
98
- <ImageWithPopover
99
- id={item.img_id}
100
- ></ImageWithPopover>
101
- <HightLightMarkdown>
102
- {item.highlight}
103
- </HightLightMarkdown>
104
- </Space>
105
- </Card>
106
- </List.Item>
107
- )}
108
- />
109
- )}
110
- {relatedQuestions?.length > 0 && (
111
- <Card>
112
- <Flex wrap="wrap" gap={'10px 0'}>
113
- {relatedQuestions?.map((x, idx) => (
114
- <Tag
115
- key={idx}
116
- className={styles.tag}
117
- onClick={handleClickRelatedQuestion(x)}
118
- >
119
- {x}
120
- </Tag>
121
- ))}
122
- </Flex>
123
- </Card>
124
- )}
125
- </section>
126
- <section className={styles.graph}>
127
- {mindMapLoading ? (
128
- <Skeleton active />
129
- ) : (
130
- <IndentedTree
131
- data={mindMap}
132
- show
133
- style={{ width: '100%', height: '100%' }}
134
- ></IndentedTree>
135
- )}
136
- </section>
137
- </Flex>
138
- )}
139
- </Content>
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  </Layout>
141
- </Layout>
 
 
 
 
 
 
142
  );
143
  };
144
 
 
19
  import { useSendQuestion } from './hooks';
20
  import SearchSidebar from './sidebar';
21
 
22
+ import PdfDrawer from '@/components/pdf-drawer';
23
+ import { useClickDrawer } from '@/components/pdf-drawer/hooks';
24
+ import RetrievalDocuments from '@/components/retrieval-documents';
25
+ import { useFetchAppConf } from '@/hooks/logic-hooks';
26
+ import { useTranslation } from 'react-i18next';
27
  import styles from './index.less';
28
 
29
  const { Content } = Layout;
30
  const { Search } = Input;
31
 
32
  const SearchPage = () => {
33
+ const { t } = useTranslation();
34
  const [checkedList, setCheckedList] = useState<string[]>([]);
35
  const list = useSelectTestingResult();
36
+ const appConf = useFetchAppConf();
37
  const {
38
  sendQuestion,
39
  handleClickRelatedQuestion,
40
  handleSearchStrChange,
41
+ handleTestChunk,
42
  answer,
43
  sendingLoading,
44
  relatedQuestions,
 
48
  loading,
49
  isFirstRender,
50
  } = useSendQuestion(checkedList);
51
+ const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } =
52
+ useClickDrawer();
53
 
54
  const InputSearch = (
55
  <Search
56
  value={searchStr}
57
  onChange={handleSearchStrChange}
58
+ placeholder={t('header.search')}
59
  allowClear
60
  enterButton
61
  onSearch={sendQuestion}
 
67
  );
68
 
69
  return (
70
+ <>
71
+ <Layout className={styles.searchPage}>
72
+ <SearchSidebar
73
+ checkedList={checkedList}
74
+ setCheckedList={setCheckedList}
75
+ ></SearchSidebar>
76
+ <Layout>
77
+ <Content>
78
+ {isFirstRender ? (
79
+ <Flex
80
+ justify="center"
81
+ align="center"
82
+ className={styles.firstRenderContent}
83
+ >
84
+ <Flex vertical align="center" gap={'large'}>
85
+ <Space size={30}>
86
+ <img src="/logo.svg" alt="" className={styles.appIcon} />
87
+ <span className={styles.appName}>{appConf.appName}</span>
88
+ </Space>
89
+ {InputSearch}
90
+ </Flex>
91
+ </Flex>
92
+ ) : (
93
+ <Flex className={styles.content}>
94
+ <section className={styles.main}>
95
+ {InputSearch}
96
+ {answer.answer && (
97
+ <div className={styles.answerWrapper}>
98
+ <MarkdownContent
99
+ loading={sendingLoading}
100
+ content={answer.answer}
101
+ reference={answer.reference ?? ({} as IReference)}
102
+ clickDocumentButton={clickDocumentButton}
103
+ ></MarkdownContent>
104
+ </div>
105
+ )}
106
+ <Divider></Divider>
107
+ <RetrievalDocuments
108
+ selectedDocumentIdsLength={0}
109
+ onTesting={handleTestChunk}
110
+ ></RetrievalDocuments>
111
+ <Divider></Divider>
112
+ {list.chunks.length > 0 && (
113
+ <List
114
+ dataSource={list.chunks}
115
+ loading={loading}
116
+ renderItem={(item) => (
117
+ <List.Item>
118
+ <Card className={styles.card}>
119
+ <Space>
120
+ <ImageWithPopover
121
+ id={item.img_id}
122
+ ></ImageWithPopover>
123
+ <HightLightMarkdown>
124
+ {item.highlight}
125
+ </HightLightMarkdown>
126
+ </Space>
127
+ </Card>
128
+ </List.Item>
129
+ )}
130
+ />
131
+ )}
132
+ {relatedQuestions?.length > 0 && (
133
+ <Card>
134
+ <Flex wrap="wrap" gap={'10px 0'}>
135
+ {relatedQuestions?.map((x, idx) => (
136
+ <Tag
137
+ key={idx}
138
+ className={styles.tag}
139
+ onClick={handleClickRelatedQuestion(x)}
140
+ >
141
+ {x}
142
+ </Tag>
143
+ ))}
144
+ </Flex>
145
+ </Card>
146
+ )}
147
+ </section>
148
+ <section className={styles.graph}>
149
+ {mindMapLoading ? (
150
+ <Skeleton active />
151
+ ) : (
152
+ <IndentedTree
153
+ data={mindMap}
154
+ show
155
+ style={{ width: '100%', height: '100%' }}
156
+ ></IndentedTree>
157
+ )}
158
+ </section>
159
+ </Flex>
160
+ )}
161
+ </Content>
162
+ </Layout>
163
  </Layout>
164
+ <PdfDrawer
165
+ visible={visible}
166
+ hideModal={hideModal}
167
+ documentId={documentId}
168
+ chunk={selectedChunk}
169
+ ></PdfDrawer>
170
+ </>
171
  );
172
  };
173