Feat: Create a folder #3221 (#7228)

### 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:
balibabu 2025-04-23 15:21:09 +08:00 committed by GitHub
parent 94181a990b
commit 51d9bde5a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 626 additions and 133 deletions

View File

@ -3,18 +3,19 @@ import React, {
ChangeEventHandler,
FunctionComponent,
PropsWithChildren,
ReactNode,
} from 'react';
import { Button, ButtonProps } from './ui/button';
import { SearchInput } from './ui/input';
interface IProps {
title: string;
showDialog?: () => void;
title?: string;
FilterPopover?: FunctionComponent<any>;
searchString?: string;
onSearchChange?: ChangeEventHandler<HTMLInputElement>;
count?: number;
showFilter?: boolean;
leftPanel?: ReactNode;
}
const FilterButton = React.forwardRef<
@ -31,16 +32,16 @@ const FilterButton = React.forwardRef<
export default function ListFilterBar({
title,
children,
showDialog,
FilterPopover,
searchString,
onSearchChange,
count,
showFilter = true,
leftPanel,
}: PropsWithChildren<IProps>) {
return (
<div className="flex justify-between mb-6">
<span className="text-3xl font-bold ">{title}</span>
<div className="flex justify-between mb-6 items-center">
<span className="text-3xl font-bold ">{leftPanel || title}</span>
<div className="flex gap-4 items-center">
{showFilter &&
(FilterPopover ? (
@ -55,9 +56,7 @@ export default function ListFilterBar({
value={searchString}
onChange={onSearchChange}
></SearchInput>
<Button variant={'tertiary'} size={'sm'} onClick={showDialog}>
{children}
</Button>
{children}
</div>
</div>
);

View 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;

View File

@ -94,6 +94,13 @@ export const useNavigatePage = () => {
[getQueryString, id, navigate],
);
const navigateToFiles = useCallback(
(folderId?: string) => {
navigate(`${Routes.Files}?folderId=${folderId}`);
},
[navigate],
);
return {
navigateToDatasetList,
navigateToDataset,
@ -109,5 +116,6 @@ export const useNavigatePage = () => {
navigateToAgentTemplates,
navigateToSearchList,
navigateToSearch,
navigateToFiles,
};
};

View File

@ -1,14 +1,26 @@
import { IFolder } from '@/interfaces/database/file-manager';
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 { useTranslation } from 'react-i18next';
import { useSearchParams } from 'umi';
import { useSetPaginationParams } from './route-hook';
export const enum FileApiAction {
UploadFile = 'uploadFile',
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 = () => {
const { setPaginationParams } = useSetPaginationParams();
const { t } = useTranslation();
@ -46,3 +58,81 @@ export const useUploadFile = () => {
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;
};

View File

@ -1,4 +1,5 @@
import ListFilterBar from '@/components/list-filter-bar';
import { Button } from '@/components/ui/button';
import { useFetchFlowList } from '@/hooks/flow-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { Plus } from 'lucide-react';
@ -11,9 +12,15 @@ export default function Agent() {
return (
<section>
<div className="px-8 pt-8">
<ListFilterBar title="Agents" showDialog={navigateToAgentTemplates}>
<Plus className="mr-2 h-4 w-4" />
Create app
<ListFilterBar title="Agents">
<Button
variant={'tertiary'}
size={'sm'}
onClick={navigateToAgentTemplates}
>
<Plus className="mr-2 h-4 w-4" />
Create app
</Button>
</ListFilterBar>
</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">

View File

@ -1,5 +1,6 @@
import { FileUploadDialog } from '@/components/file-upload-dialog';
import ListFilterBar from '@/components/list-filter-bar';
import { Button } from '@/components/ui/button';
import { Upload } from 'lucide-react';
import { DatasetTable } from './dataset-table';
import { useHandleUploadDocument } from './use-upload-document';
@ -14,9 +15,15 @@ export default function Dataset() {
} = useHandleUploadDocument();
return (
<section className="p-8">
<ListFilterBar title="Files" showDialog={showDocumentUploadModal}>
<Upload />
Upload file
<ListFilterBar title="Files">
<Button
variant={'tertiary'}
size={'sm'}
onClick={showDocumentUploadModal}
>
<Upload />
Upload file
</Button>
</ListFilterBar>
<DatasetTable></DatasetTable>

View File

@ -1,5 +1,6 @@
import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog';
import { Button } from '@/components/ui/button';
import { useFetchNextKnowledgeListByPage } from '@/hooks/use-knowledge-request';
import { pick } from 'lodash';
import { Plus } from 'lucide-react';
@ -51,7 +52,6 @@ export default function Datasets() {
<section className="p-8 text-foreground">
<ListFilterBar
title="Datasets"
showDialog={showModal}
count={ownerIds.length}
FilterPopover={({ children }: PropsWithChildren) => (
<DatasetsFilterPopover setOwnerIds={setOwnerIds} ownerIds={ownerIds}>
@ -61,7 +61,9 @@ export default function Datasets() {
searchString={searchString}
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
</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">

View File

@ -17,15 +17,18 @@ import {
UseHandleConnectToKnowledgeReturnType,
UseRenameCurrentFileReturnType,
} from './hooks';
import { UseMoveDocumentReturnType } from './use-move-file';
type IProps = Pick<CellContext<IFile, unknown>, 'row'> &
Pick<UseHandleConnectToKnowledgeReturnType, 'showConnectToKnowledgeModal'> &
Pick<UseRenameCurrentFileReturnType, 'showFileRenameModal'>;
Pick<UseRenameCurrentFileReturnType, 'showFileRenameModal'> &
Pick<UseMoveDocumentReturnType, 'showMoveFileModal'>;
export function ActionCell({
row,
showConnectToKnowledgeModal,
showFileRenameModal,
showMoveFileModal,
}: IProps) {
const { t } = useTranslation();
const record = row.original;
@ -47,6 +50,10 @@ export function ActionCell({
showFileRenameModal(record);
}, [record, showFileRenameModal]);
const handleShowMoveFileModal = useCallback(() => {
showMoveFileModal([record.id]);
}, [record, showMoveFileModal]);
return (
<section className="flex gap-4 items-center">
<Button
@ -68,9 +75,7 @@ export function ActionCell({
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(record.id)}
>
<DropdownMenuItem onClick={handleShowMoveFileModal}>
{t('common.move')}
</DropdownMenuItem>
<DropdownMenuSeparator />

View File

@ -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>
);
}

View 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>
);
}

View 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>
);
}

View File

@ -42,12 +42,11 @@ import { getExtension } from '@/utils/document-util';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { ActionCell } from './action-cell';
import {
useHandleConnectToKnowledge,
useNavigateToOtherFolder,
useRenameCurrentFile,
} from './hooks';
import { useHandleConnectToKnowledge, useRenameCurrentFile } from './hooks';
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() {
const [sorting, setSorting] = React.useState<SortingState>([]);
@ -78,6 +77,14 @@ export function FilesTable() {
fileRenameLoading,
} = useRenameCurrentFile();
const {
showMoveFileModal,
moveFileVisible,
onMoveFileOk,
hideMoveFileModal,
moveFileLoading,
} = useHandleMoveFile();
const { pagination, data, loading, setPagination } = useFetchFileList();
const columns: ColumnDef<IFile>[] = [
@ -222,6 +229,7 @@ export function FilesTable() {
row={row}
showConnectToKnowledgeModal={showConnectToKnowledgeModal}
showFileRenameModal={showFileRenameModal}
showMoveFileModal={showMoveFileModal}
></ActionCell>
);
},
@ -363,6 +371,13 @@ export function FilesTable() {
loading={fileRenameLoading}
></RenameDialog>
)}
{moveFileVisible && (
<MoveDialog
hideModal={hideMoveFileModal}
onOk={onMoveFileOk}
loading={moveFileLoading}
></MoveDialog>
)}
</div>
);
}

View File

@ -1,10 +1,7 @@
import { useSetModalState, useShowDeleteConfirm } from '@/hooks/common-hooks';
import {
useConnectToKnowledge,
useCreateFolder,
useDeleteFile,
useFetchParentFolderList,
useMoveFile,
useRenameFile,
} from '@/hooks/file-manager-hooks';
import { IFile } from '@/interfaces/database/file-manager';
@ -35,18 +32,6 @@ export const useGetRowSelection = () => {
return { rowSelection, setSelectedRowKeys };
};
export const useNavigateToOtherFolder = () => {
const navigate = useNavigate();
const navigateToOtherFolder = useCallback(
(folderId: string) => {
navigate(`/file?folderId=${folderId}`);
},
[navigate],
);
return navigateToOtherFolder;
};
export const useRenameCurrentFile = () => {
const [file, setFile] = useState<IFile>({} as IFile);
const {
@ -92,46 +77,6 @@ export type UseRenameCurrentFileReturnType = ReturnType<
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 = (
fileIds: string[],
setSelectedRowKeys: (keys: string[]) => void,
@ -222,48 +167,3 @@ export const useHandleBreadcrumbClick = () => {
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,
};
};

View File

@ -1,10 +1,23 @@
import { FileUploadDialog } from '@/components/file-upload-dialog';
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 { useTranslation } from 'react-i18next';
import { CreateFolderDialog } from './create-folder-dialog';
import { FileBreadcrumb } from './file-breadcrumb';
import { FilesTable } from './files-table';
import { useHandleCreateFolder } from './use-create-folder';
import { useHandleUploadFile } from './use-upload-file';
export default function Files() {
const { t } = useTranslation();
const {
fileUploadVisible,
hideFileUploadModal,
@ -13,11 +26,40 @@ export default function Files() {
onFileUploadOk,
} = useHandleUploadFile();
const {
folderCreateModalVisible,
showFolderCreateModal,
hideFolderCreateModal,
folderCreateLoading,
onFolderCreateOk,
} = useHandleCreateFolder();
const leftPanel = (
<div>
<FileBreadcrumb></FileBreadcrumb>
</div>
);
return (
<section className="p-8">
<ListFilterBar title="Files" showDialog={showFileUploadModal}>
<Upload />
Upload file
<ListFilterBar leftPanel={leftPanel}>
<DropdownMenu>
<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>
<FilesTable></FilesTable>
{fileUploadVisible && (
@ -27,6 +69,14 @@ export default function Files() {
loading={fileUploadLoading}
></FileUploadDialog>
)}
{folderCreateModalVisible && (
<CreateFolderDialog
loading={folderCreateLoading}
visible={folderCreateModalVisible}
hideModal={hideFolderCreateModal}
onOk={onFolderCreateOk}
></CreateFolderDialog>
)}
</section>
);
}

View 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>
);
}

View 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,
};
};

View 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>;

View 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}`,
}));
};

View File

@ -1,4 +1,5 @@
import ListFilterBar from '@/components/list-filter-bar';
import { Button } from '@/components/ui/button';
import { useFetchChatAppList } from '@/hooks/chat-hooks';
import { Plus } from 'lucide-react';
import { ChatCard } from './chat-card';
@ -9,8 +10,10 @@ export default function ChatList() {
return (
<section className="p-8">
<ListFilterBar title="Chat apps">
<Plus className="mr-2 h-4 w-4" />
Create app
<Button variant={'tertiary'} size={'sm'}>
<Plus className="mr-2 h-4 w-4" />
Create app
</Button>
</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">
{chatList.map((x) => {

View File

@ -1,4 +1,5 @@
import ListFilterBar from '@/components/list-filter-bar';
import { Button } from '@/components/ui/button';
import { useFetchFlowList } from '@/hooks/flow-hooks';
import { Plus } from 'lucide-react';
import { SearchCard } from './search-card';
@ -10,8 +11,10 @@ export default function SearchList() {
<section>
<div className="px-8 pt-8">
<ListFilterBar title="Search apps">
<Plus className="mr-2 h-4 w-4" />
Create app
<Button variant={'tertiary'} size={'sm'}>
<Plus className="mr-2 h-4 w-4" />
Create app
</Button>
</ListFilterBar>
</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">