mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-08-06 01:56:07 +08:00
### What problem does this PR solve? Feat: Create a folder #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
94181a990b
commit
51d9bde5a3
@ -3,18 +3,19 @@ import React, {
|
|||||||
ChangeEventHandler,
|
ChangeEventHandler,
|
||||||
FunctionComponent,
|
FunctionComponent,
|
||||||
PropsWithChildren,
|
PropsWithChildren,
|
||||||
|
ReactNode,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import { Button, ButtonProps } from './ui/button';
|
import { Button, ButtonProps } from './ui/button';
|
||||||
import { SearchInput } from './ui/input';
|
import { SearchInput } from './ui/input';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
title: string;
|
title?: string;
|
||||||
showDialog?: () => void;
|
|
||||||
FilterPopover?: FunctionComponent<any>;
|
FilterPopover?: FunctionComponent<any>;
|
||||||
searchString?: string;
|
searchString?: string;
|
||||||
onSearchChange?: ChangeEventHandler<HTMLInputElement>;
|
onSearchChange?: ChangeEventHandler<HTMLInputElement>;
|
||||||
count?: number;
|
count?: number;
|
||||||
showFilter?: boolean;
|
showFilter?: boolean;
|
||||||
|
leftPanel?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FilterButton = React.forwardRef<
|
const FilterButton = React.forwardRef<
|
||||||
@ -31,16 +32,16 @@ const FilterButton = React.forwardRef<
|
|||||||
export default function ListFilterBar({
|
export default function ListFilterBar({
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
showDialog,
|
|
||||||
FilterPopover,
|
FilterPopover,
|
||||||
searchString,
|
searchString,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
count,
|
count,
|
||||||
showFilter = true,
|
showFilter = true,
|
||||||
|
leftPanel,
|
||||||
}: PropsWithChildren<IProps>) {
|
}: PropsWithChildren<IProps>) {
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-between mb-6">
|
<div className="flex justify-between mb-6 items-center">
|
||||||
<span className="text-3xl font-bold ">{title}</span>
|
<span className="text-3xl font-bold ">{leftPanel || title}</span>
|
||||||
<div className="flex gap-4 items-center">
|
<div className="flex gap-4 items-center">
|
||||||
{showFilter &&
|
{showFilter &&
|
||||||
(FilterPopover ? (
|
(FilterPopover ? (
|
||||||
@ -55,9 +56,7 @@ export default function ListFilterBar({
|
|||||||
value={searchString}
|
value={searchString}
|
||||||
onChange={onSearchChange}
|
onChange={onSearchChange}
|
||||||
></SearchInput>
|
></SearchInput>
|
||||||
<Button variant={'tertiary'} size={'sm'} onClick={showDialog}>
|
{children}
|
||||||
{children}
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
95
web/src/components/ui/single-tree-select.tsx
Normal file
95
web/src/components/ui/single-tree-select.tsx
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/dropdown-menu';
|
||||||
|
import { ChevronDown, X } from 'lucide-react';
|
||||||
|
import React, { ReactNode, useState } from 'react';
|
||||||
|
|
||||||
|
export type TreeNode = {
|
||||||
|
id: number;
|
||||||
|
label: ReactNode;
|
||||||
|
children?: TreeNode[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type SingleSelectTreeDropdownProps = {
|
||||||
|
allowDelete?: boolean;
|
||||||
|
treeData: TreeNode[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const SingleTreeSelect: React.FC<SingleSelectTreeDropdownProps> = ({
|
||||||
|
allowDelete = false,
|
||||||
|
treeData,
|
||||||
|
}) => {
|
||||||
|
const [selectedOption, setSelectedOption] = useState<TreeNode | null>(null);
|
||||||
|
|
||||||
|
const handleSelect = (option: TreeNode) => {
|
||||||
|
setSelectedOption(option);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
console.log(
|
||||||
|
'Delete button clicked. Current selected option:',
|
||||||
|
selectedOption,
|
||||||
|
);
|
||||||
|
setSelectedOption(null);
|
||||||
|
console.log('After deletion, selected option:', selectedOption);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderTree = (nodes: TreeNode[]) => {
|
||||||
|
return nodes.map((node) => (
|
||||||
|
<div key={node.id} className="pl-4">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => handleSelect(node)}
|
||||||
|
className={`flex items-center ${
|
||||||
|
selectedOption?.id === node.id ? 'bg-gray-500' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span>{node.label}</span>
|
||||||
|
{node.children && (
|
||||||
|
<ChevronDown className="ml-2 h-4 w-4 text-gray-400" />
|
||||||
|
)}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
{node.children && renderTree(node.children)}
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex items-center justify-between space-x-1 p-2 border rounded-md focus:outline-none w-full"
|
||||||
|
>
|
||||||
|
{selectedOption ? (
|
||||||
|
<>
|
||||||
|
<span>{selectedOption.label}</span>
|
||||||
|
{allowDelete && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ml-2 text-gray-500 hover:text-red-500 focus:outline-none"
|
||||||
|
onClick={handleDelete}
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Select an option'
|
||||||
|
)}
|
||||||
|
<ChevronDown className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className=" mt-2 w-56 origin-top-right rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
||||||
|
{renderTree(treeData)}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SingleTreeSelect;
|
@ -94,6 +94,13 @@ export const useNavigatePage = () => {
|
|||||||
[getQueryString, id, navigate],
|
[getQueryString, id, navigate],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const navigateToFiles = useCallback(
|
||||||
|
(folderId?: string) => {
|
||||||
|
navigate(`${Routes.Files}?folderId=${folderId}`);
|
||||||
|
},
|
||||||
|
[navigate],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
navigateToDatasetList,
|
navigateToDatasetList,
|
||||||
navigateToDataset,
|
navigateToDataset,
|
||||||
@ -109,5 +116,6 @@ export const useNavigatePage = () => {
|
|||||||
navigateToAgentTemplates,
|
navigateToAgentTemplates,
|
||||||
navigateToSearchList,
|
navigateToSearchList,
|
||||||
navigateToSearch,
|
navigateToSearch,
|
||||||
|
navigateToFiles,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,26 @@
|
|||||||
|
import { IFolder } from '@/interfaces/database/file-manager';
|
||||||
import fileManagerService from '@/services/file-manager-service';
|
import fileManagerService from '@/services/file-manager-service';
|
||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSearchParams } from 'umi';
|
||||||
import { useSetPaginationParams } from './route-hook';
|
import { useSetPaginationParams } from './route-hook';
|
||||||
|
|
||||||
export const enum FileApiAction {
|
export const enum FileApiAction {
|
||||||
UploadFile = 'uploadFile',
|
UploadFile = 'uploadFile',
|
||||||
FetchFileList = 'fetchFileList',
|
FetchFileList = 'fetchFileList',
|
||||||
|
MoveFile = 'moveFile',
|
||||||
|
CreateFolder = 'createFolder',
|
||||||
|
FetchParentFolderList = 'fetchParentFolderList',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useGetFolderId = () => {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const id = searchParams.get('folderId') as string;
|
||||||
|
|
||||||
|
return id ?? '';
|
||||||
|
};
|
||||||
|
|
||||||
export const useUploadFile = () => {
|
export const useUploadFile = () => {
|
||||||
const { setPaginationParams } = useSetPaginationParams();
|
const { setPaginationParams } = useSetPaginationParams();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -46,3 +58,81 @@ export const useUploadFile = () => {
|
|||||||
|
|
||||||
return { data, loading, uploadFile: mutateAsync };
|
return { data, loading, uploadFile: mutateAsync };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IMoveFileBody {
|
||||||
|
src_file_ids: string[];
|
||||||
|
dest_file_id: string; // target folder id
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useMoveFile = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [FileApiAction.MoveFile],
|
||||||
|
mutationFn: async (params: IMoveFileBody) => {
|
||||||
|
const { data } = await fileManagerService.moveFile(params);
|
||||||
|
if (data.code === 0) {
|
||||||
|
message.success(t('message.operated'));
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [FileApiAction.FetchFileList],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data.code;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, moveFile: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateFolder = () => {
|
||||||
|
const { setPaginationParams } = useSetPaginationParams();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
isPending: loading,
|
||||||
|
mutateAsync,
|
||||||
|
} = useMutation({
|
||||||
|
mutationKey: [FileApiAction.CreateFolder],
|
||||||
|
mutationFn: async (params: { parentId: string; name: string }) => {
|
||||||
|
const { data } = await fileManagerService.createFolder({
|
||||||
|
...params,
|
||||||
|
type: 'folder',
|
||||||
|
});
|
||||||
|
if (data.code === 0) {
|
||||||
|
message.success(t('message.created'));
|
||||||
|
setPaginationParams(1);
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: [FileApiAction.FetchFileList],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return data.code;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, loading, createFolder: mutateAsync };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useFetchParentFolderList = () => {
|
||||||
|
const id = useGetFolderId();
|
||||||
|
const { data } = useQuery<IFolder[]>({
|
||||||
|
queryKey: [FileApiAction.FetchParentFolderList, id],
|
||||||
|
initialData: [],
|
||||||
|
enabled: !!id,
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await fileManagerService.getAllParentFolder({
|
||||||
|
fileId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data?.data?.parent_folders?.toReversed() ?? [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import { useFetchFlowList } from '@/hooks/flow-hooks';
|
import { useFetchFlowList } from '@/hooks/flow-hooks';
|
||||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
@ -11,9 +12,15 @@ export default function Agent() {
|
|||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<div className="px-8 pt-8">
|
<div className="px-8 pt-8">
|
||||||
<ListFilterBar title="Agents" showDialog={navigateToAgentTemplates}>
|
<ListFilterBar title="Agents">
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
<Button
|
||||||
Create app
|
variant={'tertiary'}
|
||||||
|
size={'sm'}
|
||||||
|
onClick={navigateToAgentTemplates}
|
||||||
|
>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
Create app
|
||||||
|
</Button>
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8 max-h-[84vh] overflow-auto px-8">
|
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8 max-h-[84vh] overflow-auto px-8">
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { FileUploadDialog } from '@/components/file-upload-dialog';
|
import { FileUploadDialog } from '@/components/file-upload-dialog';
|
||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import { Upload } from 'lucide-react';
|
import { Upload } from 'lucide-react';
|
||||||
import { DatasetTable } from './dataset-table';
|
import { DatasetTable } from './dataset-table';
|
||||||
import { useHandleUploadDocument } from './use-upload-document';
|
import { useHandleUploadDocument } from './use-upload-document';
|
||||||
@ -14,9 +15,15 @@ export default function Dataset() {
|
|||||||
} = useHandleUploadDocument();
|
} = useHandleUploadDocument();
|
||||||
return (
|
return (
|
||||||
<section className="p-8">
|
<section className="p-8">
|
||||||
<ListFilterBar title="Files" showDialog={showDocumentUploadModal}>
|
<ListFilterBar title="Files">
|
||||||
<Upload />
|
<Button
|
||||||
Upload file
|
variant={'tertiary'}
|
||||||
|
size={'sm'}
|
||||||
|
onClick={showDocumentUploadModal}
|
||||||
|
>
|
||||||
|
<Upload />
|
||||||
|
Upload file
|
||||||
|
</Button>
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
<DatasetTable></DatasetTable>
|
<DatasetTable></DatasetTable>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
import { RenameDialog } from '@/components/rename-dialog';
|
import { RenameDialog } from '@/components/rename-dialog';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import { useFetchNextKnowledgeListByPage } from '@/hooks/use-knowledge-request';
|
import { useFetchNextKnowledgeListByPage } from '@/hooks/use-knowledge-request';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
@ -51,7 +52,6 @@ export default function Datasets() {
|
|||||||
<section className="p-8 text-foreground">
|
<section className="p-8 text-foreground">
|
||||||
<ListFilterBar
|
<ListFilterBar
|
||||||
title="Datasets"
|
title="Datasets"
|
||||||
showDialog={showModal}
|
|
||||||
count={ownerIds.length}
|
count={ownerIds.length}
|
||||||
FilterPopover={({ children }: PropsWithChildren) => (
|
FilterPopover={({ children }: PropsWithChildren) => (
|
||||||
<DatasetsFilterPopover setOwnerIds={setOwnerIds} ownerIds={ownerIds}>
|
<DatasetsFilterPopover setOwnerIds={setOwnerIds} ownerIds={ownerIds}>
|
||||||
@ -61,7 +61,9 @@ export default function Datasets() {
|
|||||||
searchString={searchString}
|
searchString={searchString}
|
||||||
onSearchChange={handleInputChange}
|
onSearchChange={handleInputChange}
|
||||||
>
|
>
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
<Button variant={'tertiary'} size={'sm'} onClick={showModal}>
|
||||||
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
Create dataset
|
Create dataset
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
||||||
|
@ -17,15 +17,18 @@ import {
|
|||||||
UseHandleConnectToKnowledgeReturnType,
|
UseHandleConnectToKnowledgeReturnType,
|
||||||
UseRenameCurrentFileReturnType,
|
UseRenameCurrentFileReturnType,
|
||||||
} from './hooks';
|
} from './hooks';
|
||||||
|
import { UseMoveDocumentReturnType } from './use-move-file';
|
||||||
|
|
||||||
type IProps = Pick<CellContext<IFile, unknown>, 'row'> &
|
type IProps = Pick<CellContext<IFile, unknown>, 'row'> &
|
||||||
Pick<UseHandleConnectToKnowledgeReturnType, 'showConnectToKnowledgeModal'> &
|
Pick<UseHandleConnectToKnowledgeReturnType, 'showConnectToKnowledgeModal'> &
|
||||||
Pick<UseRenameCurrentFileReturnType, 'showFileRenameModal'>;
|
Pick<UseRenameCurrentFileReturnType, 'showFileRenameModal'> &
|
||||||
|
Pick<UseMoveDocumentReturnType, 'showMoveFileModal'>;
|
||||||
|
|
||||||
export function ActionCell({
|
export function ActionCell({
|
||||||
row,
|
row,
|
||||||
showConnectToKnowledgeModal,
|
showConnectToKnowledgeModal,
|
||||||
showFileRenameModal,
|
showFileRenameModal,
|
||||||
|
showMoveFileModal,
|
||||||
}: IProps) {
|
}: IProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const record = row.original;
|
const record = row.original;
|
||||||
@ -47,6 +50,10 @@ export function ActionCell({
|
|||||||
showFileRenameModal(record);
|
showFileRenameModal(record);
|
||||||
}, [record, showFileRenameModal]);
|
}, [record, showFileRenameModal]);
|
||||||
|
|
||||||
|
const handleShowMoveFileModal = useCallback(() => {
|
||||||
|
showMoveFileModal([record.id]);
|
||||||
|
}, [record, showMoveFileModal]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="flex gap-4 items-center">
|
<section className="flex gap-4 items-center">
|
||||||
<Button
|
<Button
|
||||||
@ -68,9 +75,7 @@ export function ActionCell({
|
|||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem onClick={handleShowMoveFileModal}>
|
||||||
onClick={() => navigator.clipboard.writeText(record.id)}
|
|
||||||
>
|
|
||||||
{t('common.move')}
|
{t('common.move')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Form,
|
||||||
|
FormControl,
|
||||||
|
FormField,
|
||||||
|
FormItem,
|
||||||
|
FormLabel,
|
||||||
|
FormMessage,
|
||||||
|
} from '@/components/ui/form';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export function CreateFolderForm({ hideModal, onOk }: IModalProps<any>) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const FormSchema = z.object({
|
||||||
|
name: z
|
||||||
|
.string()
|
||||||
|
.min(1, {
|
||||||
|
message: t('common.namePlaceholder'),
|
||||||
|
})
|
||||||
|
.trim(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
|
resolver: zodResolver(FormSchema),
|
||||||
|
defaultValues: { name: '' },
|
||||||
|
});
|
||||||
|
|
||||||
|
async function onSubmit(data: z.infer<typeof FormSchema>) {
|
||||||
|
const ret = await onOk?.(data.name);
|
||||||
|
if (ret) {
|
||||||
|
hideModal?.();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form {...form}>
|
||||||
|
<form
|
||||||
|
onSubmit={form.handleSubmit(onSubmit)}
|
||||||
|
className="space-y-6"
|
||||||
|
id={TagRenameId}
|
||||||
|
>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('common.name')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input
|
||||||
|
placeholder={t('common.namePlaceholder')}
|
||||||
|
{...field}
|
||||||
|
autoComplete="off"
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
36
web/src/pages/files/create-folder-dialog/index.tsx
Normal file
36
web/src/pages/files/create-folder-dialog/index.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import { LoadingButton } from '@/components/ui/loading-button';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { TagRenameId } from '@/pages/add-knowledge/constant';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { CreateFolderForm } from './create-folder-form';
|
||||||
|
|
||||||
|
export function CreateFolderDialog({
|
||||||
|
hideModal,
|
||||||
|
onOk,
|
||||||
|
loading,
|
||||||
|
}: IModalProps<any>) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open onOpenChange={hideModal}>
|
||||||
|
<DialogContent className="sm:max-w-[425px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{t('fileManager.newFolder')}</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<CreateFolderForm hideModal={hideModal} onOk={onOk}></CreateFolderForm>
|
||||||
|
<DialogFooter>
|
||||||
|
<LoadingButton type="submit" form={TagRenameId} loading={loading}>
|
||||||
|
{t('common.save')}
|
||||||
|
</LoadingButton>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
36
web/src/pages/files/file-breadcrumb.tsx
Normal file
36
web/src/pages/files/file-breadcrumb.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
Breadcrumb,
|
||||||
|
BreadcrumbItem,
|
||||||
|
BreadcrumbList,
|
||||||
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
|
} from '@/components/ui/breadcrumb';
|
||||||
|
import { useNavigate } from 'umi';
|
||||||
|
import { useSelectBreadcrumbItems } from './use-navigate-to-folder';
|
||||||
|
|
||||||
|
export function FileBreadcrumb() {
|
||||||
|
const breadcrumbItems = useSelectBreadcrumbItems();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<Breadcrumb>
|
||||||
|
<BreadcrumbList>
|
||||||
|
{breadcrumbItems.map((x, idx) => (
|
||||||
|
<div key={x.path} className="flex items-center gap-2">
|
||||||
|
{idx !== 0 && <BreadcrumbSeparator />}
|
||||||
|
<BreadcrumbItem
|
||||||
|
key={x.path}
|
||||||
|
onClick={() => navigate(x.path)}
|
||||||
|
className="cursor-pointer"
|
||||||
|
>
|
||||||
|
{idx === breadcrumbItems.length - 1 ? (
|
||||||
|
<BreadcrumbPage>{x.title}</BreadcrumbPage>
|
||||||
|
) : (
|
||||||
|
x.title
|
||||||
|
)}
|
||||||
|
</BreadcrumbItem>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</BreadcrumbList>
|
||||||
|
</Breadcrumb>
|
||||||
|
);
|
||||||
|
}
|
@ -42,12 +42,11 @@ import { getExtension } from '@/utils/document-util';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ActionCell } from './action-cell';
|
import { ActionCell } from './action-cell';
|
||||||
import {
|
import { useHandleConnectToKnowledge, useRenameCurrentFile } from './hooks';
|
||||||
useHandleConnectToKnowledge,
|
|
||||||
useNavigateToOtherFolder,
|
|
||||||
useRenameCurrentFile,
|
|
||||||
} from './hooks';
|
|
||||||
import { LinkToDatasetDialog } from './link-to-dataset-dialog';
|
import { LinkToDatasetDialog } from './link-to-dataset-dialog';
|
||||||
|
import { MoveDialog } from './move-dialog';
|
||||||
|
import { useHandleMoveFile } from './use-move-file';
|
||||||
|
import { useNavigateToOtherFolder } from './use-navigate-to-folder';
|
||||||
|
|
||||||
export function FilesTable() {
|
export function FilesTable() {
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
@ -78,6 +77,14 @@ export function FilesTable() {
|
|||||||
fileRenameLoading,
|
fileRenameLoading,
|
||||||
} = useRenameCurrentFile();
|
} = useRenameCurrentFile();
|
||||||
|
|
||||||
|
const {
|
||||||
|
showMoveFileModal,
|
||||||
|
moveFileVisible,
|
||||||
|
onMoveFileOk,
|
||||||
|
hideMoveFileModal,
|
||||||
|
moveFileLoading,
|
||||||
|
} = useHandleMoveFile();
|
||||||
|
|
||||||
const { pagination, data, loading, setPagination } = useFetchFileList();
|
const { pagination, data, loading, setPagination } = useFetchFileList();
|
||||||
|
|
||||||
const columns: ColumnDef<IFile>[] = [
|
const columns: ColumnDef<IFile>[] = [
|
||||||
@ -222,6 +229,7 @@ export function FilesTable() {
|
|||||||
row={row}
|
row={row}
|
||||||
showConnectToKnowledgeModal={showConnectToKnowledgeModal}
|
showConnectToKnowledgeModal={showConnectToKnowledgeModal}
|
||||||
showFileRenameModal={showFileRenameModal}
|
showFileRenameModal={showFileRenameModal}
|
||||||
|
showMoveFileModal={showMoveFileModal}
|
||||||
></ActionCell>
|
></ActionCell>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -363,6 +371,13 @@ export function FilesTable() {
|
|||||||
loading={fileRenameLoading}
|
loading={fileRenameLoading}
|
||||||
></RenameDialog>
|
></RenameDialog>
|
||||||
)}
|
)}
|
||||||
|
{moveFileVisible && (
|
||||||
|
<MoveDialog
|
||||||
|
hideModal={hideMoveFileModal}
|
||||||
|
onOk={onMoveFileOk}
|
||||||
|
loading={moveFileLoading}
|
||||||
|
></MoveDialog>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks';
|
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks';
|
||||||
import {
|
import {
|
||||||
useConnectToKnowledge,
|
useConnectToKnowledge,
|
||||||
useCreateFolder,
|
|
||||||
useDeleteFile,
|
useDeleteFile,
|
||||||
useFetchParentFolderList,
|
|
||||||
useMoveFile,
|
|
||||||
useRenameFile,
|
useRenameFile,
|
||||||
} from '@/hooks/file-manager-hooks';
|
} from '@/hooks/file-manager-hooks';
|
||||||
import { IFile } from '@/interfaces/database/file-manager';
|
import { IFile } from '@/interfaces/database/file-manager';
|
||||||
@ -35,18 +32,6 @@ export const useGetRowSelection = () => {
|
|||||||
return { rowSelection, setSelectedRowKeys };
|
return { rowSelection, setSelectedRowKeys };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useNavigateToOtherFolder = () => {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const navigateToOtherFolder = useCallback(
|
|
||||||
(folderId: string) => {
|
|
||||||
navigate(`/file?folderId=${folderId}`);
|
|
||||||
},
|
|
||||||
[navigate],
|
|
||||||
);
|
|
||||||
|
|
||||||
return navigateToOtherFolder;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useRenameCurrentFile = () => {
|
export const useRenameCurrentFile = () => {
|
||||||
const [file, setFile] = useState<IFile>({} as IFile);
|
const [file, setFile] = useState<IFile>({} as IFile);
|
||||||
const {
|
const {
|
||||||
@ -92,46 +77,6 @@ export type UseRenameCurrentFileReturnType = ReturnType<
|
|||||||
typeof useRenameCurrentFile
|
typeof useRenameCurrentFile
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const useSelectBreadcrumbItems = () => {
|
|
||||||
const parentFolderList = useFetchParentFolderList();
|
|
||||||
|
|
||||||
return parentFolderList.length === 1
|
|
||||||
? []
|
|
||||||
: parentFolderList.map((x) => ({
|
|
||||||
title: x.name === '/' ? 'root' : x.name,
|
|
||||||
path: `/file?folderId=${x.id}`,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useHandleCreateFolder = () => {
|
|
||||||
const {
|
|
||||||
visible: folderCreateModalVisible,
|
|
||||||
hideModal: hideFolderCreateModal,
|
|
||||||
showModal: showFolderCreateModal,
|
|
||||||
} = useSetModalState();
|
|
||||||
const { createFolder, loading } = useCreateFolder();
|
|
||||||
const id = useGetFolderId();
|
|
||||||
|
|
||||||
const onFolderCreateOk = useCallback(
|
|
||||||
async (name: string) => {
|
|
||||||
const ret = await createFolder({ parentId: id, name });
|
|
||||||
|
|
||||||
if (ret === 0) {
|
|
||||||
hideFolderCreateModal();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[createFolder, hideFolderCreateModal, id],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
folderCreateLoading: loading,
|
|
||||||
onFolderCreateOk,
|
|
||||||
folderCreateModalVisible,
|
|
||||||
hideFolderCreateModal,
|
|
||||||
showFolderCreateModal,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useHandleDeleteFile = (
|
export const useHandleDeleteFile = (
|
||||||
fileIds: string[],
|
fileIds: string[],
|
||||||
setSelectedRowKeys: (keys: string[]) => void,
|
setSelectedRowKeys: (keys: string[]) => void,
|
||||||
@ -222,48 +167,3 @@ export const useHandleBreadcrumbClick = () => {
|
|||||||
|
|
||||||
return { handleBreadcrumbClick };
|
return { handleBreadcrumbClick };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useHandleMoveFile = (
|
|
||||||
setSelectedRowKeys: (keys: string[]) => void,
|
|
||||||
) => {
|
|
||||||
const {
|
|
||||||
visible: moveFileVisible,
|
|
||||||
hideModal: hideMoveFileModal,
|
|
||||||
showModal: showMoveFileModal,
|
|
||||||
} = useSetModalState();
|
|
||||||
const { moveFile, loading } = useMoveFile();
|
|
||||||
const [sourceFileIds, setSourceFileIds] = useState<string[]>([]);
|
|
||||||
|
|
||||||
const onMoveFileOk = useCallback(
|
|
||||||
async (targetFolderId: string) => {
|
|
||||||
const ret = await moveFile({
|
|
||||||
src_file_ids: sourceFileIds,
|
|
||||||
dest_file_id: targetFolderId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (ret === 0) {
|
|
||||||
setSelectedRowKeys([]);
|
|
||||||
hideMoveFileModal();
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
[moveFile, hideMoveFileModal, sourceFileIds, setSelectedRowKeys],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleShowMoveFileModal = useCallback(
|
|
||||||
(ids: string[]) => {
|
|
||||||
setSourceFileIds(ids);
|
|
||||||
showMoveFileModal();
|
|
||||||
},
|
|
||||||
[showMoveFileModal],
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
initialValue: '',
|
|
||||||
moveFileLoading: loading,
|
|
||||||
onMoveFileOk,
|
|
||||||
moveFileVisible,
|
|
||||||
hideMoveFileModal,
|
|
||||||
showMoveFileModal: handleShowMoveFileModal,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
@ -1,10 +1,23 @@
|
|||||||
import { FileUploadDialog } from '@/components/file-upload-dialog';
|
import { FileUploadDialog } from '@/components/file-upload-dialog';
|
||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { Upload } from 'lucide-react';
|
import { Upload } from 'lucide-react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { CreateFolderDialog } from './create-folder-dialog';
|
||||||
|
import { FileBreadcrumb } from './file-breadcrumb';
|
||||||
import { FilesTable } from './files-table';
|
import { FilesTable } from './files-table';
|
||||||
|
import { useHandleCreateFolder } from './use-create-folder';
|
||||||
import { useHandleUploadFile } from './use-upload-file';
|
import { useHandleUploadFile } from './use-upload-file';
|
||||||
|
|
||||||
export default function Files() {
|
export default function Files() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const {
|
const {
|
||||||
fileUploadVisible,
|
fileUploadVisible,
|
||||||
hideFileUploadModal,
|
hideFileUploadModal,
|
||||||
@ -13,11 +26,40 @@ export default function Files() {
|
|||||||
onFileUploadOk,
|
onFileUploadOk,
|
||||||
} = useHandleUploadFile();
|
} = useHandleUploadFile();
|
||||||
|
|
||||||
|
const {
|
||||||
|
folderCreateModalVisible,
|
||||||
|
showFolderCreateModal,
|
||||||
|
hideFolderCreateModal,
|
||||||
|
folderCreateLoading,
|
||||||
|
onFolderCreateOk,
|
||||||
|
} = useHandleCreateFolder();
|
||||||
|
|
||||||
|
const leftPanel = (
|
||||||
|
<div>
|
||||||
|
<FileBreadcrumb></FileBreadcrumb>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="p-8">
|
<section className="p-8">
|
||||||
<ListFilterBar title="Files" showDialog={showFileUploadModal}>
|
<ListFilterBar leftPanel={leftPanel}>
|
||||||
<Upload />
|
<DropdownMenu>
|
||||||
Upload file
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant={'tertiary'} size={'sm'}>
|
||||||
|
<Upload />
|
||||||
|
{t('knowledgeDetails.addFile')}
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="w-56">
|
||||||
|
<DropdownMenuItem onClick={showFileUploadModal}>
|
||||||
|
{t('fileManager.uploadFile')}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem onClick={showFolderCreateModal}>
|
||||||
|
{t('fileManager.newFolder')}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
<FilesTable></FilesTable>
|
<FilesTable></FilesTable>
|
||||||
{fileUploadVisible && (
|
{fileUploadVisible && (
|
||||||
@ -27,6 +69,14 @@ export default function Files() {
|
|||||||
loading={fileUploadLoading}
|
loading={fileUploadLoading}
|
||||||
></FileUploadDialog>
|
></FileUploadDialog>
|
||||||
)}
|
)}
|
||||||
|
{folderCreateModalVisible && (
|
||||||
|
<CreateFolderDialog
|
||||||
|
loading={folderCreateLoading}
|
||||||
|
visible={folderCreateModalVisible}
|
||||||
|
hideModal={hideFolderCreateModal}
|
||||||
|
onOk={onFolderCreateOk}
|
||||||
|
></CreateFolderDialog>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
56
web/src/pages/files/move-dialog.tsx
Normal file
56
web/src/pages/files/move-dialog.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '@/components/ui/dialog';
|
||||||
|
import SingleTreeSelect, { TreeNode } from '@/components/ui/single-tree-select';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
export function MoveDialog({ hideModal }: IModalProps<any>) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const treeData: TreeNode[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
label: 'Node 1',
|
||||||
|
children: [
|
||||||
|
{ id: 11, label: 'Node 1.1' },
|
||||||
|
{ id: 12, label: 'Node 1.2' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
label: 'Node 2',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 21,
|
||||||
|
label: 'Node 2.1',
|
||||||
|
children: [
|
||||||
|
{ id: 211, label: 'Node 2.1.1' },
|
||||||
|
{ id: 212, label: 'Node 2.1.2' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open onOpenChange={hideModal}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>{t('common.move')}</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div>
|
||||||
|
<SingleTreeSelect treeData={treeData}></SingleTreeSelect>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="submit">Save changes</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
33
web/src/pages/files/use-create-folder.ts
Normal file
33
web/src/pages/files/use-create-folder.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
|
import { useCreateFolder } from '@/hooks/use-file-request';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useGetFolderId } from './hooks';
|
||||||
|
|
||||||
|
export const useHandleCreateFolder = () => {
|
||||||
|
const {
|
||||||
|
visible: folderCreateModalVisible,
|
||||||
|
hideModal: hideFolderCreateModal,
|
||||||
|
showModal: showFolderCreateModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
const { createFolder, loading } = useCreateFolder();
|
||||||
|
const id = useGetFolderId();
|
||||||
|
|
||||||
|
const onFolderCreateOk = useCallback(
|
||||||
|
async (name: string) => {
|
||||||
|
const ret = await createFolder({ parentId: id, name });
|
||||||
|
|
||||||
|
if (ret === 0) {
|
||||||
|
hideFolderCreateModal();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[createFolder, hideFolderCreateModal, id],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
folderCreateLoading: loading,
|
||||||
|
onFolderCreateOk,
|
||||||
|
folderCreateModalVisible,
|
||||||
|
hideFolderCreateModal,
|
||||||
|
showFolderCreateModal,
|
||||||
|
};
|
||||||
|
};
|
50
web/src/pages/files/use-move-file.ts
Normal file
50
web/src/pages/files/use-move-file.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
|
import { useMoveFile } from '@/hooks/use-file-request';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
export const useHandleMoveFile = () =>
|
||||||
|
// setSelectedRowKeys: (keys: string[]) => void,
|
||||||
|
{
|
||||||
|
const {
|
||||||
|
visible: moveFileVisible,
|
||||||
|
hideModal: hideMoveFileModal,
|
||||||
|
showModal: showMoveFileModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
const { moveFile, loading } = useMoveFile();
|
||||||
|
const [sourceFileIds, setSourceFileIds] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const onMoveFileOk = useCallback(
|
||||||
|
async (targetFolderId: string) => {
|
||||||
|
const ret = await moveFile({
|
||||||
|
src_file_ids: sourceFileIds,
|
||||||
|
dest_file_id: targetFolderId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ret === 0) {
|
||||||
|
// setSelectedRowKeys([]);
|
||||||
|
hideMoveFileModal();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
},
|
||||||
|
[moveFile, hideMoveFileModal, sourceFileIds],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleShowMoveFileModal = useCallback(
|
||||||
|
(ids: string[]) => {
|
||||||
|
setSourceFileIds(ids);
|
||||||
|
showMoveFileModal();
|
||||||
|
},
|
||||||
|
[showMoveFileModal],
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
initialValue: '',
|
||||||
|
moveFileLoading: loading,
|
||||||
|
onMoveFileOk,
|
||||||
|
moveFileVisible,
|
||||||
|
hideMoveFileModal,
|
||||||
|
showMoveFileModal: handleShowMoveFileModal,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UseMoveDocumentReturnType = ReturnType<typeof useHandleMoveFile>;
|
28
web/src/pages/files/use-navigate-to-folder.ts
Normal file
28
web/src/pages/files/use-navigate-to-folder.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
|
import { useFetchParentFolderList } from '@/hooks/use-file-request';
|
||||||
|
import { Routes } from '@/routes';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
export const useNavigateToOtherFolder = () => {
|
||||||
|
const { navigateToFiles } = useNavigatePage();
|
||||||
|
|
||||||
|
const navigateToOtherFolder = useCallback(
|
||||||
|
(folderId: string) => {
|
||||||
|
navigateToFiles(folderId);
|
||||||
|
},
|
||||||
|
[navigateToFiles],
|
||||||
|
);
|
||||||
|
|
||||||
|
return navigateToOtherFolder;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSelectBreadcrumbItems = () => {
|
||||||
|
const parentFolderList = useFetchParentFolderList();
|
||||||
|
|
||||||
|
return parentFolderList.length === 1
|
||||||
|
? []
|
||||||
|
: parentFolderList.map((x) => ({
|
||||||
|
title: x.name === '/' ? 'root' : x.name,
|
||||||
|
path: `${Routes.Files}?folderId=${x.id}`,
|
||||||
|
}));
|
||||||
|
};
|
@ -1,4 +1,5 @@
|
|||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import { useFetchChatAppList } from '@/hooks/chat-hooks';
|
import { useFetchChatAppList } from '@/hooks/chat-hooks';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { ChatCard } from './chat-card';
|
import { ChatCard } from './chat-card';
|
||||||
@ -9,8 +10,10 @@ export default function ChatList() {
|
|||||||
return (
|
return (
|
||||||
<section className="p-8">
|
<section className="p-8">
|
||||||
<ListFilterBar title="Chat apps">
|
<ListFilterBar title="Chat apps">
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
<Button variant={'tertiary'} size={'sm'}>
|
||||||
Create app
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
Create app
|
||||||
|
</Button>
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8">
|
||||||
{chatList.map((x) => {
|
{chatList.map((x) => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import ListFilterBar from '@/components/list-filter-bar';
|
import ListFilterBar from '@/components/list-filter-bar';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import { useFetchFlowList } from '@/hooks/flow-hooks';
|
import { useFetchFlowList } from '@/hooks/flow-hooks';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { SearchCard } from './search-card';
|
import { SearchCard } from './search-card';
|
||||||
@ -10,8 +11,10 @@ export default function SearchList() {
|
|||||||
<section>
|
<section>
|
||||||
<div className="px-8 pt-8">
|
<div className="px-8 pt-8">
|
||||||
<ListFilterBar title="Search apps">
|
<ListFilterBar title="Search apps">
|
||||||
<Plus className="mr-2 h-4 w-4" />
|
<Button variant={'tertiary'} size={'sm'}>
|
||||||
Create app
|
<Plus className="mr-2 h-4 w-4" />
|
||||||
|
Create app
|
||||||
|
</Button>
|
||||||
</ListFilterBar>
|
</ListFilterBar>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8 max-h-[84vh] overflow-auto px-8">
|
<div className="grid gap-6 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6 2xl:grid-cols-8 max-h-[84vh] overflow-auto px-8">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user