Feat: Create empty document. #3221 (#7343)

### What problem does this PR solve?

Feat: Create empty document. #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2025-04-27 16:12:10 +08:00 committed by GitHub
parent bdebd1b2e3
commit 5043143bc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 137 additions and 62 deletions

View File

@ -8,6 +8,7 @@ import {
import { LoadingButton } from '@/components/ui/loading-button';
import { IModalProps } from '@/interfaces/common';
import { TagRenameId } from '@/pages/add-knowledge/constant';
import { ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { RenameForm } from './rename-form';
@ -16,14 +17,15 @@ export function RenameDialog({
initialName,
onOk,
loading,
}: IModalProps<any> & { initialName?: string }) {
title,
}: IModalProps<any> & { initialName?: string; title?: ReactNode }) {
const { t } = useTranslation();
return (
<Dialog open onOpenChange={hideModal}>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>{t('common.rename')}</DialogTitle>
<DialogTitle>{title || t('common.rename')}</DialogTitle>
</DialogHeader>
<RenameForm
initialName={initialName}

View File

@ -16,7 +16,10 @@ import {
useGetPaginationWithRouter,
useHandleSearchChange,
} from './logic-hooks';
import { useGetKnowledgeSearchParams } from './route-hook';
import {
useGetKnowledgeSearchParams,
useSetPaginationParams,
} from './route-hook';
export const enum DocumentApiAction {
UploadDocument = 'uploadDocument',
@ -28,6 +31,7 @@ export const enum DocumentApiAction {
SetDocumentParser = 'setDocumentParser',
SetDocumentMeta = 'setDocumentMeta',
FetchAllDocumentList = 'fetchAllDocumentList',
CreateDocument = 'createDocument',
}
export const useUploadNextDocument = () => {
@ -363,3 +367,37 @@ export const useSetDocumentMeta = () => {
return { setDocumentMeta: mutateAsync, data, loading };
};
export const useCreateDocument = () => {
const { id } = useParams();
const { setPaginationParams, page } = useSetPaginationParams();
const queryClient = useQueryClient();
const {
data,
isPending: loading,
mutateAsync,
} = useMutation({
mutationKey: [DocumentApiAction.CreateDocument],
mutationFn: async (name: string) => {
const { data } = await kbService.document_create({
name,
kb_id: id,
});
if (data.code === 0) {
if (page === 1) {
queryClient.invalidateQueries({
queryKey: [DocumentApiAction.FetchDocumentList],
});
} else {
setPaginationParams(); // fetch document list
}
message.success(i18n.t('message.created'));
}
return data.code;
},
});
return { createDocument: mutateAsync, loading, data };
};

View File

@ -5,6 +5,7 @@ import {
HoverCardContent,
HoverCardTrigger,
} from '@/components/ui/hover-card';
import { DocumentType } from '@/constants/knowledge';
import { useRemoveDocument } from '@/hooks/use-document-request';
import { IDocumentInfo } from '@/interfaces/database/document';
import { formatFileSize } from '@/utils/common-util';
@ -27,8 +28,9 @@ export function DatasetActionCell({
record,
showRenameModal,
}: { record: IDocumentInfo } & UseRenameDocumentShowType) {
const { id, run } = record;
const { id, run, type } = record;
const isRunning = isParserRunning(run);
const isVirtualDocument = type === DocumentType.Virtual;
const { removeDocument } = useRemoveDocument();
@ -83,14 +85,16 @@ export function DatasetActionCell({
>
<Pencil />
</Button>
<Button
variant={'ghost'}
size={'icon'}
onClick={onDownloadDocument}
disabled={isRunning}
>
<ArrowDownToLine />
</Button>
{isVirtualDocument || (
<Button
variant={'ghost'}
size={'icon'}
onClick={onDownloadDocument}
disabled={isRunning}
>
<ArrowDownToLine />
</Button>
)}
<ConfirmDeleteDialog onOk={handleRemove}>
<Button variant={'ghost'} size={'icon'} disabled={isRunning}>
<Trash2 className="text-text-delete-red" />

View File

@ -24,9 +24,7 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table';
import { useSetSelectedRecord } from '@/hooks/logic-hooks';
import { useFetchDocumentList } from '@/hooks/use-document-request';
import { IDocumentInfo } from '@/interfaces/database/document';
import { getExtension } from '@/utils/document-util';
import { useMemo } from 'react';
import { SetMetaDialog } from './set-meta-dialog';
@ -53,8 +51,6 @@ export function DatasetTable({
React.useState<VisibilityState>({});
const [rowSelection, setRowSelection] = React.useState({});
const { currentRecord, setRecord } = useSetSelectedRecord<IDocumentInfo>();
const {
changeParserLoading,
onChangeParserOk,
@ -84,7 +80,6 @@ export function DatasetTable({
const columns = useDatasetTableColumns({
showChangeParserModal,
setCurrentRecord: setRecord,
showRenameModal,
showSetMetaModal,
});

View File

@ -1,5 +1,5 @@
import { useSetModalState } from '@/hooks/common-hooks';
import { useCreateNextDocument, useNextWebCrawl } from '@/hooks/document-hooks';
import { useNextWebCrawl } from '@/hooks/document-hooks';
import { useGetKnowledgeSearchParams } from '@/hooks/route-hook';
import { useCallback, useState } from 'react';
import { useNavigate } from 'umi';
@ -17,34 +17,6 @@ export const useNavigateToOtherPage = () => {
return { linkToUploadPage, toChunk };
};
export const useCreateEmptyDocument = () => {
const { createDocument, loading } = useCreateNextDocument();
const {
visible: createVisible,
hideModal: hideCreateModal,
showModal: showCreateModal,
} = useSetModalState();
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 useGetRowSelection = () => {
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);

View File

@ -1,15 +1,26 @@
import { BulkOperateBar } from '@/components/bulk-operate-bar';
import { FileUploadDialog } from '@/components/file-upload-dialog';
import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useFetchDocumentList } from '@/hooks/use-document-request';
import { Upload } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { DatasetTable } from './dataset-table';
import { useBulkOperateDataset } from './use-bulk-operate-dataset';
import { useCreateEmptyDocument } from './use-create-empty-document';
import { useSelectDatasetFilters } from './use-select-filters';
import { useHandleUploadDocument } from './use-upload-document';
export default function Dataset() {
const { t } = useTranslation();
const {
documentUploadVisible,
hideDocumentUploadModal,
@ -29,6 +40,14 @@ export default function Dataset() {
} = useFetchDocumentList();
const { filters } = useSelectDatasetFilters();
const {
createLoading,
onCreateOk,
createVisible,
hideCreateModal,
showCreateModal,
} = useCreateEmptyDocument();
return (
<section className="p-8">
<ListFilterBar
@ -39,14 +58,23 @@ export default function Dataset() {
onChange={handleFilterSubmit}
filters={filters}
>
<Button
variant={'tertiary'}
size={'sm'}
onClick={showDocumentUploadModal}
>
<Upload />
Upload file
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant={'tertiary'} size={'sm'}>
<Upload />
{t('knowledgeDetails.addFile')}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuItem onClick={showDocumentUploadModal}>
{t('fileManager.uploadFile')}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={showCreateModal}>
{t('fileManager.newFolder')}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ListFilterBar>
<BulkOperateBar list={list}></BulkOperateBar>
<DatasetTable
@ -61,6 +89,14 @@ export default function Dataset() {
loading={documentUploadLoading}
></FileUploadDialog>
)}
{createVisible && (
<RenameDialog
hideModal={hideCreateModal}
onOk={onCreateOk}
loading={createLoading}
title={'File Name'}
></RenameDialog>
)}
</section>
);
}

View File

@ -0,0 +1,31 @@
import { useSetModalState } from '@/hooks/common-hooks';
import { useCreateDocument } from '@/hooks/use-document-request';
import { useCallback } from 'react';
export const useCreateEmptyDocument = () => {
const { createDocument, loading } = useCreateDocument();
const {
visible: createVisible,
hideModal: hideCreateModal,
showModal: showCreateModal,
} = useSetModalState();
const onCreateOk = useCallback(
async (name: string) => {
const ret = await createDocument(name);
if (ret === 0) {
hideCreateModal();
}
},
[hideCreateModal, createDocument],
);
return {
createLoading: loading,
onCreateOk,
createVisible,
hideCreateModal,
showCreateModal,
};
};

View File

@ -22,14 +22,12 @@ import { UseChangeDocumentParserShowType } from './use-change-document-parser';
import { UseRenameDocumentShowType } from './use-rename-document';
import { UseSaveMetaShowType } from './use-save-meta';
type UseDatasetTableColumnsType = UseChangeDocumentParserShowType & {
setCurrentRecord: (record: IDocumentInfo) => void;
} & UseRenameDocumentShowType &
type UseDatasetTableColumnsType = UseChangeDocumentParserShowType &
UseRenameDocumentShowType &
UseSaveMetaShowType;
export function useDatasetTableColumns({
showChangeParserModal,
setCurrentRecord,
showRenameModal,
showSetMetaModal,
}: UseDatasetTableColumnsType) {

View File

@ -31,7 +31,7 @@ export function SideBar() {
const { data } = useFetchKnowledgeBaseConfiguration();
return (
<aside className="w-[303px] relative border-r ">
<aside className="w-60 relative border-r ">
<div className="p-6 space-y-2 border-b">
<div
className="w-[70px] h-[70px] rounded-xl bg-cover"
@ -56,9 +56,6 @@ export function SideBar() {
>
<item.icon className="w-6 h-6" />
<span>{item.label}</span>
{active && (
<div className="absolute right-0 w-[5px] h-[66px] bg-primary rounded-l-xl shadow-[0_0_5.94px_#7561ff,0_0_11.88px_#7561ff,0_0_41.58px_#7561ff,0_0_83.16px_#7561ff,0_0_142.56px_#7561ff,0_0_249.48px_#7561ff]" />
)}
</Button>
);
})}

View File

@ -71,6 +71,8 @@
--sidebar-accent-foreground: 240 5.9% 10%;
--sidebar-border: 220 13% 91%;
--sidebar-ring: 217.2 91.2% 59.8%;
--background-inverse-strong: rgba(255, 255, 255, 0.15);
}
.dark {