From b6837d4ca6f4476bc72eadc31990aaf2d00f0d57 Mon Sep 17 00:00:00 2001
From: balibabu <cike8899@users.noreply.github.com>
Date: Thu, 28 Mar 2024 16:18:16 +0800
Subject: [PATCH] feat: add corresponding icons to files (#164)

---
 web/src/components/new-document-link.tsx      |  8 +-
 web/src/hooks/knowledgeHook.ts                | 23 +++++
 .../components/knowledge-chunk/index.less     |  1 +
 .../components/knowledge-file/index.less      |  1 -
 .../components/knowledge-file/index.tsx       | 21 +++--
 .../components/knowledge-testing/index.tsx    | 17 ++--
 .../testing-control/index.tsx                 |  7 +-
 .../testing-result/index.less                 | 10 ++-
 .../testing-result/index.tsx                  |  2 +-
 .../testing-result/select-files.tsx           |  5 +-
 web/src/pages/chat/chat-container/index.less  |  3 +
 web/src/pages/chat/chat-container/index.tsx   | 90 +++++++++++++------
 web/src/utils/documentUtils.ts                |  4 +
 13 files changed, 137 insertions(+), 55 deletions(-)

diff --git a/web/src/components/new-document-link.tsx b/web/src/components/new-document-link.tsx
index 84c0c93..e64ef14 100644
--- a/web/src/components/new-document-link.tsx
+++ b/web/src/components/new-document-link.tsx
@@ -3,12 +3,18 @@ import React from 'react';
 
 interface IProps extends React.PropsWithChildren {
   documentId: string;
+  preventDefault?: boolean;
 }
 
-const NewDocumentLink = ({ children, documentId }: IProps) => {
+const NewDocumentLink = ({
+  children,
+  documentId,
+  preventDefault = false,
+}: IProps) => {
   return (
     <a
       target="_blank"
+      onClick={!preventDefault ? undefined : (e) => e.preventDefault()}
       href={`${api_host}/document/get/${documentId}`}
       rel="noreferrer"
     >
diff --git a/web/src/hooks/knowledgeHook.ts b/web/src/hooks/knowledgeHook.ts
index 1ef5167..8a0f7a3 100644
--- a/web/src/hooks/knowledgeHook.ts
+++ b/web/src/hooks/knowledgeHook.ts
@@ -206,3 +206,26 @@ export const useSelectKnowledgeDetails = () => {
   return knowledgeDetails;
 };
 //#endregion
+
+//#region Retrieval testing
+
+export const useTestChunkRetrieval = () => {
+  const dispatch = useDispatch();
+  const knowledgeBaseId = useKnowledgeBaseId();
+
+  const testChunk = useCallback(
+    (values: any) => {
+      dispatch({
+        type: 'testingModel/testDocumentChunk',
+        payload: {
+          ...values,
+          kb_id: knowledgeBaseId,
+        },
+      });
+    },
+    [dispatch, knowledgeBaseId],
+  );
+
+  return testChunk;
+};
+//#endregion
diff --git a/web/src/pages/add-knowledge/components/knowledge-chunk/index.less b/web/src/pages/add-knowledge/components/knowledge-chunk/index.less
index b7380ed..a41a35a 100644
--- a/web/src/pages/add-knowledge/components/knowledge-chunk/index.less
+++ b/web/src/pages/add-knowledge/components/knowledge-chunk/index.less
@@ -37,6 +37,7 @@
   }
 
   .chunkContainer {
+    display: flex;
     height: calc(100vh - 332px);
   }
 
diff --git a/web/src/pages/add-knowledge/components/knowledge-file/index.less b/web/src/pages/add-knowledge/components/knowledge-file/index.less
index 05614a1..d78737b 100644
--- a/web/src/pages/add-knowledge/components/knowledge-file/index.less
+++ b/web/src/pages/add-knowledge/components/knowledge-file/index.less
@@ -22,7 +22,6 @@
 .img {
   height: 24px;
   width: 24px;
-  margin-right: 10px;
   display: inline-block;
   vertical-align: middle;
 }
diff --git a/web/src/pages/add-knowledge/components/knowledge-file/index.tsx b/web/src/pages/add-knowledge/components/knowledge-file/index.tsx
index 73200c2..6fa7f20 100644
--- a/web/src/pages/add-knowledge/components/knowledge-file/index.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-file/index.tsx
@@ -1,9 +1,12 @@
+import ChunkMethodModal from '@/components/chunk-method-modal';
+import SvgIcon from '@/components/svg-icon';
 import {
   useSelectDocumentList,
   useSetDocumentStatus,
 } from '@/hooks/documentHooks';
 import { useSelectParserList } from '@/hooks/userSettingHook';
 import { IKnowledgeFile } from '@/interfaces/database/knowledge';
+import { getExtension } from '@/utils/documentUtils';
 import {
   FileOutlined,
   FileTextOutlined,
@@ -15,6 +18,7 @@ import {
   Button,
   Divider,
   Dropdown,
+  Flex,
   Input,
   Space,
   Switch,
@@ -38,8 +42,6 @@ import ParsingActionCell from './parsing-action-cell';
 import ParsingStatusCell from './parsing-status-cell';
 import RenameModal from './rename-modal';
 
-import ChunkMethodModal from '@/components/chunk-method-modal';
-import { getExtension } from '@/utils/documentUtils';
 import styles from './index.less';
 
 const KnowledgeFile = () => {
@@ -114,10 +116,19 @@ const KnowledgeFile = () => {
       dataIndex: 'name',
       key: 'name',
       fixed: 'left',
-      render: (text: any, { id, thumbnail }) => (
+      render: (text: any, { id, thumbnail, name }) => (
         <div className={styles.tochunks} onClick={() => toChunk(id)}>
-          <img className={styles.img} src={thumbnail} alt="" />
-          {text}
+          <Flex gap={10} align="center">
+            {thumbnail ? (
+              <img className={styles.img} src={thumbnail} alt="" />
+            ) : (
+              <SvgIcon
+                name={`file-icon/${getExtension(name)}`}
+                width={24}
+              ></SvgIcon>
+            )}
+            {text}
+          </Flex>
         </div>
       ),
     },
diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx
index 4b449f2..4716861 100644
--- a/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-testing/index.tsx
@@ -1,28 +1,21 @@
+import { useTestChunkRetrieval } from '@/hooks/knowledgeHook';
 import { Flex, Form } from 'antd';
+import { useEffect } from 'react';
+import { useDispatch } from 'umi';
 import TestingControl from './testing-control';
 import TestingResult from './testing-result';
 
-import { useKnowledgeBaseId } from '@/hooks/knowledgeHook';
-import { useEffect } from 'react';
-import { useDispatch } from 'umi';
 import styles from './index.less';
 
 const KnowledgeTesting = () => {
   const [form] = Form.useForm();
+  const testChunk = useTestChunkRetrieval();
 
   const dispatch = useDispatch();
-  const knowledgeBaseId = useKnowledgeBaseId();
 
   const handleTesting = async () => {
     const values = await form.validateFields();
-    console.info(values);
-    dispatch({
-      type: 'testingModel/testDocumentChunk',
-      payload: {
-        ...values,
-        kb_id: knowledgeBaseId,
-      },
-    });
+    testChunk(values);
   };
 
   useEffect(() => {
diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx
index 4630e2e..a2d915b 100644
--- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-control/index.tsx
@@ -2,10 +2,9 @@ import SimilaritySlider from '@/components/similarity-slider';
 import { Button, Card, Divider, Flex, Form, Input, Slider, Tag } from 'antd';
 import { FormInstance } from 'antd/lib';
 
+import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks';
 import styles from './index.less';
 
-const list = [1, 2, 3];
-
 type FieldType = {
   similarity_threshold?: number;
   vector_similarity_weight?: number;
@@ -20,6 +19,9 @@ interface IProps {
 
 const TestingControl = ({ form, handleTesting }: IProps) => {
   const question = Form.useWatch('question', { form, preserve: true });
+  const loading = useOneNamespaceEffectsLoading('testingModel', [
+    'testDocumentChunk',
+  ]);
 
   const buttonDisabled =
     !question || (typeof question === 'string' && question.trim() === '');
@@ -65,6 +67,7 @@ const TestingControl = ({ form, handleTesting }: IProps) => {
                 size="small"
                 onClick={handleTesting}
                 disabled={buttonDisabled}
+                loading={loading}
               >
                 Testing
               </Button>
diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less
index 1059af5..464d306 100644
--- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less
+++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.less
@@ -35,9 +35,11 @@
   }
   .image {
     width: 100px;
+    object-fit: contain;
   }
-  .imagePreview {
-    display: block;
-    width: 260px;
-  }
+}
+.imagePreview {
+  display: block;
+  max-width: 45vw;
+  max-height: 40vh;
 }
diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx
index 9277418..1dd4b5f 100644
--- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/index.tsx
@@ -104,7 +104,7 @@ const TestingResult = ({ handleTesting }: IProps) => {
             <Flex gap={'middle'}>
               {x.img_id && (
                 <Popover
-                  placement="topRight"
+                  placement="left"
                   content={
                     <Image
                       id={x.img_id}
diff --git a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx
index cb80d01..a0e2816 100644
--- a/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx
+++ b/web/src/pages/add-knowledge/components/knowledge-testing/testing-result/select-files.tsx
@@ -1,6 +1,7 @@
 import { ReactComponent as NavigationPointerIcon } from '@/assets/svg/navigation-pointer.svg';
 import NewDocumentLink from '@/components/new-document-link';
 import { ITestingDocument } from '@/interfaces/database/knowledge';
+import { isPdf } from '@/utils/documentUtils';
 import { Table, TableProps } from 'antd';
 import { useDispatch, useSelector } from 'umi';
 
@@ -33,8 +34,8 @@ const SelectFiles = ({ handleTesting }: IProps) => {
       title: 'View',
       key: 'view',
       width: 50,
-      render: (_, { doc_id }) => (
-        <NewDocumentLink documentId={doc_id}>
+      render: (_, { doc_id, doc_name }) => (
+        <NewDocumentLink documentId={doc_id} preventDefault={!isPdf(doc_name)}>
           <NavigationPointerIcon />
         </NewDocumentLink>
       ),
diff --git a/web/src/pages/chat/chat-container/index.less b/web/src/pages/chat/chat-container/index.less
index e13b72d..b521e4b 100644
--- a/web/src/pages/chat/chat-container/index.less
+++ b/web/src/pages/chat/chat-container/index.less
@@ -64,3 +64,6 @@
   max-height: 45vh;
   overflow-y: auto;
 }
+.documentLink {
+  padding: 0;
+}
diff --git a/web/src/pages/chat/chat-container/index.tsx b/web/src/pages/chat/chat-container/index.tsx
index a927121..887e92f 100644
--- a/web/src/pages/chat/chat-container/index.tsx
+++ b/web/src/pages/chat/chat-container/index.tsx
@@ -36,6 +36,8 @@ import {
   useSendMessage,
 } from '../hooks';
 
+import SvgIcon from '@/components/svg-icon';
+import { getExtension, isPdf } from '@/utils/documentUtils';
 import styles from './index.less';
 
 const reg = /(#{2}\d+\${2})/g;
@@ -74,7 +76,10 @@ const MessageItem = ({
   const isAssistant = item.role === MessageType.Assistant;
 
   const handleDocumentButtonClick = useCallback(
-    (documentId: string, chunk: IChunk) => () => {
+    (documentId: string, chunk: IChunk, isPdf: boolean) => () => {
+      if (!isPdf) {
+        return;
+      }
       clickDocumentButton(documentId, chunk);
     },
     [clickDocumentButton],
@@ -88,26 +93,31 @@ const MessageItem = ({
         (x) => x?.doc_id === chunkItem?.doc_id,
       );
       const documentId = document?.doc_id;
+      const fileThumbnail = documentId ? fileThumbnails[documentId] : '';
+      const fileExtension = documentId ? getExtension(document?.doc_name) : '';
+      const imageId = chunkItem?.img_id;
       return (
         <Flex
           key={chunkItem?.chunk_id}
           gap={10}
           className={styles.referencePopoverWrapper}
         >
-          <Popover
-            placement="left"
-            content={
+          {imageId && (
+            <Popover
+              placement="left"
+              content={
+                <Image
+                  id={imageId}
+                  className={styles.referenceImagePreview}
+                ></Image>
+              }
+            >
               <Image
-                id={chunkItem?.img_id}
-                className={styles.referenceImagePreview}
+                id={imageId}
+                className={styles.referenceChunkImage}
               ></Image>
-            }
-          >
-            <Image
-              id={chunkItem?.img_id}
-              className={styles.referenceChunkImage}
-            ></Image>
-          </Popover>
+            </Popover>
+          )}
           <Space direction={'vertical'}>
             <div
               dangerouslySetInnerHTML={{
@@ -116,11 +126,23 @@ const MessageItem = ({
               className={styles.chunkContentText}
             ></div>
             {documentId && (
-              <Flex gap={'middle'}>
-                <img src={fileThumbnails[documentId]} alt="" />
+              <Flex gap={'small'}>
+                {fileThumbnail ? (
+                  <img src={fileThumbnail} alt="" />
+                ) : (
+                  <SvgIcon
+                    name={`file-icon/${fileExtension}`}
+                    width={24}
+                  ></SvgIcon>
+                )}
                 <Button
                   type="link"
-                  onClick={handleDocumentButtonClick(documentId, chunkItem)}
+                  className={styles.documentLink}
+                  onClick={handleDocumentButtonClick(
+                    documentId,
+                    chunkItem,
+                    fileExtension === 'pdf',
+                  )}
                 >
                   {document?.doc_name}
                 </Button>
@@ -224,17 +246,31 @@ const MessageItem = ({
               <List
                 bordered
                 dataSource={referenceDocumentList}
-                renderItem={(item) => (
-                  <List.Item>
-                    {/* <SvgIcon name={getFileIcon(item.doc_name)}></SvgIcon> */}
-                    <Flex gap={'middle'}>
-                      <img src={fileThumbnails[item.doc_id]}></img>
-                      <NewDocumentLink documentId={item.doc_id}>
-                        {item.doc_name}
-                      </NewDocumentLink>
-                    </Flex>
-                  </List.Item>
-                )}
+                renderItem={(item) => {
+                  const fileThumbnail = fileThumbnails[item.doc_id];
+                  const fileExtension = getExtension(item.doc_name);
+                  return (
+                    <List.Item>
+                      <Flex gap={'small'} align="center">
+                        {fileThumbnail ? (
+                          <img src={fileThumbnail}></img>
+                        ) : (
+                          <SvgIcon
+                            name={`file-icon/${fileExtension}`}
+                            width={24}
+                          ></SvgIcon>
+                        )}
+
+                        <NewDocumentLink
+                          documentId={item.doc_id}
+                          preventDefault={!isPdf(item.doc_name)}
+                        >
+                          {item.doc_name}
+                        </NewDocumentLink>
+                      </Flex>
+                    </List.Item>
+                  );
+                }}
               />
             )}
           </Flex>
diff --git a/web/src/utils/documentUtils.ts b/web/src/utils/documentUtils.ts
index 27e9964..f99d005 100644
--- a/web/src/utils/documentUtils.ts
+++ b/web/src/utils/documentUtils.ts
@@ -38,3 +38,7 @@ export const isFileUploadDone = (file: UploadFile) => file.status === 'done';
 
 export const getExtension = (name: string) =>
   name?.slice(name.lastIndexOf('.') + 1).toLowerCase() ?? '';
+
+export const isPdf = (name: string) => {
+  return getExtension(name) === 'pdf';
+};
-- 
GitLab