### What problem does this PR solve? Feat: Rename document name #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.16.0
| @@ -1,49 +1,39 @@ | |||
| import { Button } from '@/components/ui/button'; | |||
| import { | |||
| Dialog, | |||
| DialogContent, | |||
| DialogFooter, | |||
| DialogHeader, | |||
| DialogTitle, | |||
| DialogTrigger, | |||
| } from '@/components/ui/dialog'; | |||
| import { Input } from '@/components/ui/input'; | |||
| import { Label } from '@/components/ui/label'; | |||
| 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 { RenameForm } from './rename-form'; | |||
| export function RenameDialog({ | |||
| hideModal, | |||
| initialName, | |||
| onOk, | |||
| loading, | |||
| }: IModalProps<any> & { initialName: string }) { | |||
| const { t } = useTranslation(); | |||
| export function RenameDialog() { | |||
| return ( | |||
| <Dialog> | |||
| <DialogTrigger asChild> | |||
| <Button variant="outline">Edit Profile</Button> | |||
| </DialogTrigger> | |||
| <Dialog open onOpenChange={hideModal}> | |||
| <DialogContent className="sm:max-w-[425px]"> | |||
| <DialogHeader> | |||
| <DialogTitle>Edit profile</DialogTitle> | |||
| <DialogTitle>{t('common.rename')}</DialogTitle> | |||
| </DialogHeader> | |||
| <div className="grid gap-4 py-4"> | |||
| <div className="grid grid-cols-4 items-center gap-4"> | |||
| <Label htmlFor="name" className="text-right"> | |||
| Name | |||
| </Label> | |||
| <Input | |||
| id="name" | |||
| defaultValue="Pedro Duarte" | |||
| className="col-span-3" | |||
| /> | |||
| </div> | |||
| <div className="grid grid-cols-4 items-center gap-4"> | |||
| <Label htmlFor="username" className="text-right"> | |||
| Username | |||
| </Label> | |||
| <Input | |||
| id="username" | |||
| defaultValue="@peduarte" | |||
| className="col-span-3" | |||
| /> | |||
| </div> | |||
| </div> | |||
| <RenameForm | |||
| initialName={initialName} | |||
| hideModal={hideModal} | |||
| onOk={onOk} | |||
| ></RenameForm> | |||
| <DialogFooter> | |||
| <Button type="submit">Save changes</Button> | |||
| <LoadingButton type="submit" form={TagRenameId} loading={loading}> | |||
| {t('common.save')} | |||
| </LoadingButton> | |||
| </DialogFooter> | |||
| </DialogContent> | |||
| </Dialog> | |||
| @@ -0,0 +1,79 @@ | |||
| '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 { useEffect } from 'react'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| export function RenameForm({ | |||
| initialName, | |||
| hideModal, | |||
| onOk, | |||
| }: IModalProps<any> & { initialName: string }) { | |||
| 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?.(); | |||
| } | |||
| } | |||
| useEffect(() => { | |||
| form.setValue('name', initialName); | |||
| }, [form, initialName]); | |||
| 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> | |||
| ); | |||
| } | |||
| @@ -4,26 +4,49 @@ import { | |||
| DropdownMenu, | |||
| DropdownMenuContent, | |||
| DropdownMenuItem, | |||
| DropdownMenuLabel, | |||
| DropdownMenuSeparator, | |||
| DropdownMenuTrigger, | |||
| } from '@/components/ui/dropdown-menu'; | |||
| import { useDownloadFile } from '@/hooks/file-manager-hooks'; | |||
| import { IFile } from '@/interfaces/database/file-manager'; | |||
| import { CellContext } from '@tanstack/react-table'; | |||
| import { EllipsisVertical, Link2, Trash2 } from 'lucide-react'; | |||
| import { useCallback } from 'react'; | |||
| import { UseHandleConnectToKnowledgeReturnType } from './hooks'; | |||
| import { useTranslation } from 'react-i18next'; | |||
| import { | |||
| UseHandleConnectToKnowledgeReturnType, | |||
| UseRenameCurrentFileReturnType, | |||
| } from './hooks'; | |||
| type IProps = Pick<CellContext<IFile, unknown>, 'row'> & | |||
| Pick<UseHandleConnectToKnowledgeReturnType, 'showConnectToKnowledgeModal'>; | |||
| Pick<UseHandleConnectToKnowledgeReturnType, 'showConnectToKnowledgeModal'> & | |||
| Pick<UseRenameCurrentFileReturnType, 'showFileRenameModal'>; | |||
| export function ActionCell({ row, showConnectToKnowledgeModal }: IProps) { | |||
| export function ActionCell({ | |||
| row, | |||
| showConnectToKnowledgeModal, | |||
| showFileRenameModal, | |||
| }: IProps) { | |||
| const { t } = useTranslation(); | |||
| const record = row.original; | |||
| const documentId = record.id; | |||
| const { downloadFile } = useDownloadFile(); | |||
| const handleShowConnectToKnowledgeModal = useCallback(() => { | |||
| showConnectToKnowledgeModal(record); | |||
| }, [record, showConnectToKnowledgeModal]); | |||
| const onDownloadDocument = useCallback(() => { | |||
| downloadFile({ | |||
| id: documentId, | |||
| filename: record.name, | |||
| }); | |||
| }, [documentId, downloadFile, record.name]); | |||
| const handleShowFileRenameModal = useCallback(() => { | |||
| showFileRenameModal(record); | |||
| }, [record, showFileRenameModal]); | |||
| return ( | |||
| <section className="flex gap-4 items-center"> | |||
| <Button | |||
| @@ -45,15 +68,19 @@ export function ActionCell({ row, showConnectToKnowledgeModal }: IProps) { | |||
| </Button> | |||
| </DropdownMenuTrigger> | |||
| <DropdownMenuContent align="end"> | |||
| <DropdownMenuLabel>Actions</DropdownMenuLabel> | |||
| <DropdownMenuItem | |||
| onClick={() => navigator.clipboard.writeText(record.id)} | |||
| > | |||
| Copy payment ID | |||
| {t('common.move')} | |||
| </DropdownMenuItem> | |||
| <DropdownMenuSeparator /> | |||
| <DropdownMenuItem>View customer</DropdownMenuItem> | |||
| <DropdownMenuItem>View payment details</DropdownMenuItem> | |||
| <DropdownMenuItem onClick={handleShowFileRenameModal}> | |||
| {t('common.rename')} | |||
| </DropdownMenuItem> | |||
| <DropdownMenuSeparator /> | |||
| <DropdownMenuItem onClick={onDownloadDocument}> | |||
| {t('common.download')} | |||
| </DropdownMenuItem> | |||
| </DropdownMenuContent> | |||
| </DropdownMenu> | |||
| </section> | |||
| @@ -14,6 +14,7 @@ import { | |||
| import { ArrowUpDown } from 'lucide-react'; | |||
| import * as React from 'react'; | |||
| import { RenameDialog } from '@/components/rename-dialog'; | |||
| import SvgIcon from '@/components/svg-icon'; | |||
| import { TableEmpty, TableSkeleton } from '@/components/table-skeleton'; | |||
| import { Badge } from '@/components/ui/badge'; | |||
| @@ -41,7 +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 } from './hooks'; | |||
| import { | |||
| useHandleConnectToKnowledge, | |||
| useNavigateToOtherFolder, | |||
| useRenameCurrentFile, | |||
| } from './hooks'; | |||
| import { LinkToDatasetDialog } from './link-to-dataset-dialog'; | |||
| export function FilesTable() { | |||
| @@ -64,6 +69,14 @@ export function FilesTable() { | |||
| onConnectToKnowledgeOk, | |||
| connectToKnowledgeLoading, | |||
| } = useHandleConnectToKnowledge(); | |||
| const { | |||
| fileRenameVisible, | |||
| showFileRenameModal, | |||
| hideFileRenameModal, | |||
| onFileRenameOk, | |||
| initialFileName, | |||
| fileRenameLoading, | |||
| } = useRenameCurrentFile(); | |||
| const { pagination, data, loading, setPagination } = useFetchFileList(); | |||
| @@ -208,6 +221,7 @@ export function FilesTable() { | |||
| <ActionCell | |||
| row={row} | |||
| showConnectToKnowledgeModal={showConnectToKnowledgeModal} | |||
| showFileRenameModal={showFileRenameModal} | |||
| ></ActionCell> | |||
| ); | |||
| }, | |||
| @@ -341,6 +355,14 @@ export function FilesTable() { | |||
| loading={connectToKnowledgeLoading} | |||
| ></LinkToDatasetDialog> | |||
| )} | |||
| {fileRenameVisible && ( | |||
| <RenameDialog | |||
| hideModal={hideFileRenameModal} | |||
| onOk={onFileRenameOk} | |||
| initialName={initialFileName} | |||
| loading={fileRenameLoading} | |||
| ></RenameDialog> | |||
| )} | |||
| </div> | |||
| ); | |||
| } | |||
| @@ -90,6 +90,10 @@ export const useRenameCurrentFile = () => { | |||
| }; | |||
| }; | |||
| export type UseRenameCurrentFileReturnType = ReturnType< | |||
| typeof useRenameCurrentFile | |||
| >; | |||
| export const useSelectBreadcrumbItems = () => { | |||
| const parentFolderList = useFetchParentFolderList(); | |||