balibabu
commited on
Commit
·
0dad3f5
1
Parent(s):
29e6d3f
feat: The search box is displayed globally when the page is loaded for the first time #2247 (#2325)
Browse files### What problem does this PR solve?
feat: The search box is displayed globally when the page is loaded for
the first time #2247
### Type of change
- [x] New Feature (non-breaking change which adds functionality)
- web/src/pages/search/hooks.ts +12 -7
- web/src/pages/search/index.less +31 -2
- web/src/pages/search/index.tsx +100 -65
- web/src/pages/search/sidebar.tsx +2 -1
web/src/pages/search/hooks.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { useTestChunkRetrieval } from '@/hooks/knowledge-hooks';
|
|
| 3 |
import { useSendMessageWithSse } from '@/hooks/logic-hooks';
|
| 4 |
import { IAnswer } from '@/interfaces/database/chat';
|
| 5 |
import api from '@/utils/api';
|
| 6 |
-
import { isEmpty } from 'lodash';
|
| 7 |
import { ChangeEventHandler, useCallback, useEffect, useState } from 'react';
|
| 8 |
|
| 9 |
export const useSendQuestion = (kbIds: string[]) => {
|
|
@@ -19,18 +19,22 @@ export const useSendQuestion = (kbIds: string[]) => {
|
|
| 19 |
loading: mindMapLoading,
|
| 20 |
} = useFetchMindMap();
|
| 21 |
const [searchStr, setSearchStr] = useState<string>('');
|
|
|
|
| 22 |
|
| 23 |
const sendQuestion = useCallback(
|
| 24 |
(question: string) => {
|
|
|
|
|
|
|
|
|
|
| 25 |
setCurrentAnswer({} as IAnswer);
|
| 26 |
setSendingLoading(true);
|
| 27 |
-
send({ kb_ids: kbIds, question });
|
| 28 |
-
testChunk({ kb_id: kbIds, highlight: true, question });
|
| 29 |
fetchMindMap({
|
| 30 |
-
question,
|
| 31 |
kb_ids: kbIds,
|
| 32 |
});
|
| 33 |
-
fetchRelatedQuestions(
|
| 34 |
},
|
| 35 |
[send, testChunk, kbIds, fetchRelatedQuestions, fetchMindMap],
|
| 36 |
);
|
|
@@ -65,14 +69,15 @@ export const useSendQuestion = (kbIds: string[]) => {
|
|
| 65 |
|
| 66 |
return {
|
| 67 |
sendQuestion,
|
|
|
|
|
|
|
| 68 |
loading,
|
| 69 |
sendingLoading,
|
| 70 |
answer: currentAnswer,
|
| 71 |
relatedQuestions: relatedQuestions?.slice(0, 5) ?? [],
|
| 72 |
mindMap,
|
| 73 |
mindMapLoading,
|
| 74 |
-
handleClickRelatedQuestion,
|
| 75 |
searchStr,
|
| 76 |
-
|
| 77 |
};
|
| 78 |
};
|
|
|
|
| 3 |
import { useSendMessageWithSse } from '@/hooks/logic-hooks';
|
| 4 |
import { IAnswer } from '@/interfaces/database/chat';
|
| 5 |
import api from '@/utils/api';
|
| 6 |
+
import { isEmpty, trim } from 'lodash';
|
| 7 |
import { ChangeEventHandler, useCallback, useEffect, useState } from 'react';
|
| 8 |
|
| 9 |
export const useSendQuestion = (kbIds: string[]) => {
|
|
|
|
| 19 |
loading: mindMapLoading,
|
| 20 |
} = useFetchMindMap();
|
| 21 |
const [searchStr, setSearchStr] = useState<string>('');
|
| 22 |
+
const [isFirstRender, setIsFirstRender] = useState(true);
|
| 23 |
|
| 24 |
const sendQuestion = useCallback(
|
| 25 |
(question: string) => {
|
| 26 |
+
const q = trim(question);
|
| 27 |
+
if (isEmpty(q)) return;
|
| 28 |
+
setIsFirstRender(false);
|
| 29 |
setCurrentAnswer({} as IAnswer);
|
| 30 |
setSendingLoading(true);
|
| 31 |
+
send({ kb_ids: kbIds, question: q });
|
| 32 |
+
testChunk({ kb_id: kbIds, highlight: true, question: q });
|
| 33 |
fetchMindMap({
|
| 34 |
+
question: q,
|
| 35 |
kb_ids: kbIds,
|
| 36 |
});
|
| 37 |
+
fetchRelatedQuestions(q);
|
| 38 |
},
|
| 39 |
[send, testChunk, kbIds, fetchRelatedQuestions, fetchMindMap],
|
| 40 |
);
|
|
|
|
| 69 |
|
| 70 |
return {
|
| 71 |
sendQuestion,
|
| 72 |
+
handleSearchStrChange,
|
| 73 |
+
handleClickRelatedQuestion,
|
| 74 |
loading,
|
| 75 |
sendingLoading,
|
| 76 |
answer: currentAnswer,
|
| 77 |
relatedQuestions: relatedQuestions?.slice(0, 5) ?? [],
|
| 78 |
mindMap,
|
| 79 |
mindMapLoading,
|
|
|
|
| 80 |
searchStr,
|
| 81 |
+
isFirstRender,
|
| 82 |
};
|
| 83 |
};
|
web/src/pages/search/index.less
CHANGED
|
@@ -49,18 +49,22 @@
|
|
| 49 |
}
|
| 50 |
}
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
.content {
|
| 53 |
height: 100%;
|
| 54 |
.main {
|
| 55 |
width: 60%;
|
| 56 |
// background-color: aqua;
|
| 57 |
overflow: auto;
|
| 58 |
-
padding: 10px;
|
| 59 |
}
|
| 60 |
|
| 61 |
.graph {
|
| 62 |
width: 40%;
|
| 63 |
-
padding
|
| 64 |
}
|
| 65 |
}
|
| 66 |
.answerWrapper {
|
|
@@ -72,3 +76,28 @@
|
|
| 72 |
margin: 0;
|
| 73 |
}
|
| 74 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
}
|
| 50 |
}
|
| 51 |
|
| 52 |
+
.firstRenderContent {
|
| 53 |
+
height: 100%;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
.content {
|
| 57 |
height: 100%;
|
| 58 |
.main {
|
| 59 |
width: 60%;
|
| 60 |
// background-color: aqua;
|
| 61 |
overflow: auto;
|
| 62 |
+
padding: 20px 10px 10px;
|
| 63 |
}
|
| 64 |
|
| 65 |
.graph {
|
| 66 |
width: 40%;
|
| 67 |
+
padding: 20px 10px 10px;
|
| 68 |
}
|
| 69 |
}
|
| 70 |
.answerWrapper {
|
|
|
|
| 76 |
margin: 0;
|
| 77 |
}
|
| 78 |
}
|
| 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 |
+
}
|
| 89 |
+
button {
|
| 90 |
+
height: 50px !important;
|
| 91 |
+
border-start-end-radius: 30px !important;
|
| 92 |
+
border-end-end-radius: 30px !important;
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
.globalInput {
|
| 97 |
+
width: 600px;
|
| 98 |
+
.input();
|
| 99 |
+
}
|
| 100 |
+
.partialInput {
|
| 101 |
+
width: 100%;
|
| 102 |
+
.input();
|
| 103 |
+
}
|
web/src/pages/search/index.tsx
CHANGED
|
@@ -1,14 +1,24 @@
|
|
| 1 |
import HightLightMarkdown from '@/components/highlight-markdown';
|
| 2 |
import { ImageWithPopover } from '@/components/image';
|
|
|
|
| 3 |
import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
|
| 4 |
import { IReference } from '@/interfaces/database/chat';
|
| 5 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
import { useState } from 'react';
|
| 7 |
import MarkdownContent from '../chat/markdown-content';
|
| 8 |
import { useSendQuestion } from './hooks';
|
| 9 |
import SearchSidebar from './sidebar';
|
| 10 |
|
| 11 |
-
import IndentedTree from '@/components/indented-tree/indented-tree';
|
| 12 |
import styles from './index.less';
|
| 13 |
|
| 14 |
const { Content } = Layout;
|
|
@@ -27,8 +37,25 @@ const SearchPage = () => {
|
|
| 27 |
mindMap,
|
| 28 |
mindMapLoading,
|
| 29 |
searchStr,
|
|
|
|
|
|
|
| 30 |
} = useSendQuestion(checkedList);
|
| 31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
return (
|
| 33 |
<Layout className={styles.searchPage}>
|
| 34 |
<SearchSidebar
|
|
@@ -37,70 +64,78 @@ const SearchPage = () => {
|
|
| 37 |
></SearchSidebar>
|
| 38 |
<Layout>
|
| 39 |
<Content>
|
| 40 |
-
|
| 41 |
-
<
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
<
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
<List
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
)}
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
<Flex wrap="wrap" gap={'10px 0'}>
|
| 79 |
-
{relatedQuestions?.map((x, idx) => (
|
| 80 |
-
<Tag
|
| 81 |
-
key={idx}
|
| 82 |
-
className={styles.tag}
|
| 83 |
-
onClick={handleClickRelatedQuestion(x)}
|
| 84 |
-
>
|
| 85 |
-
{x}
|
| 86 |
-
</Tag>
|
| 87 |
-
))}
|
| 88 |
-
</Flex>
|
| 89 |
-
</Card>
|
| 90 |
-
)}
|
| 91 |
-
</section>
|
| 92 |
-
<section className={styles.graph}>
|
| 93 |
-
{mindMapLoading ? (
|
| 94 |
-
<Skeleton active />
|
| 95 |
-
) : (
|
| 96 |
-
<IndentedTree
|
| 97 |
-
data={mindMap}
|
| 98 |
-
show
|
| 99 |
-
style={{ width: '100%', height: '100%' }}
|
| 100 |
-
></IndentedTree>
|
| 101 |
-
)}
|
| 102 |
-
</section>
|
| 103 |
-
</Flex>
|
| 104 |
</Content>
|
| 105 |
</Layout>
|
| 106 |
</Layout>
|
|
|
|
| 1 |
import HightLightMarkdown from '@/components/highlight-markdown';
|
| 2 |
import { ImageWithPopover } from '@/components/image';
|
| 3 |
+
import IndentedTree from '@/components/indented-tree/indented-tree';
|
| 4 |
import { useSelectTestingResult } from '@/hooks/knowledge-hooks';
|
| 5 |
import { IReference } from '@/interfaces/database/chat';
|
| 6 |
+
import {
|
| 7 |
+
Card,
|
| 8 |
+
Divider,
|
| 9 |
+
Flex,
|
| 10 |
+
Input,
|
| 11 |
+
Layout,
|
| 12 |
+
List,
|
| 13 |
+
Skeleton,
|
| 14 |
+
Space,
|
| 15 |
+
Tag,
|
| 16 |
+
} from 'antd';
|
| 17 |
import { useState } from 'react';
|
| 18 |
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;
|
|
|
|
| 37 |
mindMap,
|
| 38 |
mindMapLoading,
|
| 39 |
searchStr,
|
| 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}
|
| 52 |
+
size="large"
|
| 53 |
+
loading={sendingLoading}
|
| 54 |
+
disabled={checkedList.length === 0}
|
| 55 |
+
className={isFirstRender ? styles.globalInput : styles.partialInput}
|
| 56 |
+
/>
|
| 57 |
+
);
|
| 58 |
+
|
| 59 |
return (
|
| 60 |
<Layout className={styles.searchPage}>
|
| 61 |
<SearchSidebar
|
|
|
|
| 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>
|
web/src/pages/search/sidebar.tsx
CHANGED
|
@@ -22,7 +22,7 @@ interface IProps {
|
|
| 22 |
}
|
| 23 |
|
| 24 |
const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => {
|
| 25 |
-
const { list } = useNextFetchKnowledgeList();
|
| 26 |
const ids = useMemo(() => list.map((x) => x.id), [list]);
|
| 27 |
|
| 28 |
const checkAll = list.length === checkedList.length;
|
|
@@ -67,6 +67,7 @@ const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => {
|
|
| 67 |
bordered
|
| 68 |
dataSource={list}
|
| 69 |
className={styles.list}
|
|
|
|
| 70 |
renderItem={(item) => (
|
| 71 |
<List.Item>
|
| 72 |
<Checkbox value={item.id} className={styles.checkbox}>
|
|
|
|
| 22 |
}
|
| 23 |
|
| 24 |
const SearchSidebar = ({ checkedList, setCheckedList }: IProps) => {
|
| 25 |
+
const { list, loading } = useNextFetchKnowledgeList();
|
| 26 |
const ids = useMemo(() => list.map((x) => x.id), [list]);
|
| 27 |
|
| 28 |
const checkAll = list.length === checkedList.length;
|
|
|
|
| 67 |
bordered
|
| 68 |
dataSource={list}
|
| 69 |
className={styles.list}
|
| 70 |
+
loading={loading}
|
| 71 |
renderItem={(item) => (
|
| 72 |
<List.Item>
|
| 73 |
<Checkbox value={item.id} className={styles.checkbox}>
|