### 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
| import { Button } from '@/components/ui/button'; | |||||
| import { | import { | ||||
| Dialog, | Dialog, | ||||
| DialogContent, | DialogContent, | ||||
| DialogFooter, | DialogFooter, | ||||
| DialogHeader, | DialogHeader, | ||||
| DialogTitle, | DialogTitle, | ||||
| DialogTrigger, | |||||
| } from '@/components/ui/dialog'; | } 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 ( | return ( | ||||
| <Dialog> | |||||
| <DialogTrigger asChild> | |||||
| <Button variant="outline">Edit Profile</Button> | |||||
| </DialogTrigger> | |||||
| <Dialog open onOpenChange={hideModal}> | |||||
| <DialogContent className="sm:max-w-[425px]"> | <DialogContent className="sm:max-w-[425px]"> | ||||
| <DialogHeader> | <DialogHeader> | ||||
| <DialogTitle>Edit profile</DialogTitle> | |||||
| <DialogTitle>{t('common.rename')}</DialogTitle> | |||||
| </DialogHeader> | </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> | <DialogFooter> | ||||
| <Button type="submit">Save changes</Button> | |||||
| <LoadingButton type="submit" form={TagRenameId} loading={loading}> | |||||
| {t('common.save')} | |||||
| </LoadingButton> | |||||
| </DialogFooter> | </DialogFooter> | ||||
| </DialogContent> | </DialogContent> | ||||
| </Dialog> | </Dialog> |
| '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> | |||||
| ); | |||||
| } |
| DropdownMenu, | DropdownMenu, | ||||
| DropdownMenuContent, | DropdownMenuContent, | ||||
| DropdownMenuItem, | DropdownMenuItem, | ||||
| DropdownMenuLabel, | |||||
| DropdownMenuSeparator, | DropdownMenuSeparator, | ||||
| DropdownMenuTrigger, | DropdownMenuTrigger, | ||||
| } from '@/components/ui/dropdown-menu'; | } from '@/components/ui/dropdown-menu'; | ||||
| import { useDownloadFile } from '@/hooks/file-manager-hooks'; | |||||
| import { IFile } from '@/interfaces/database/file-manager'; | import { IFile } from '@/interfaces/database/file-manager'; | ||||
| import { CellContext } from '@tanstack/react-table'; | import { CellContext } from '@tanstack/react-table'; | ||||
| import { EllipsisVertical, Link2, Trash2 } from 'lucide-react'; | import { EllipsisVertical, Link2, Trash2 } from 'lucide-react'; | ||||
| import { useCallback } from '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'> & | 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 record = row.original; | ||||
| const documentId = record.id; | |||||
| const { downloadFile } = useDownloadFile(); | |||||
| const handleShowConnectToKnowledgeModal = useCallback(() => { | const handleShowConnectToKnowledgeModal = useCallback(() => { | ||||
| showConnectToKnowledgeModal(record); | showConnectToKnowledgeModal(record); | ||||
| }, [record, showConnectToKnowledgeModal]); | }, [record, showConnectToKnowledgeModal]); | ||||
| const onDownloadDocument = useCallback(() => { | |||||
| downloadFile({ | |||||
| id: documentId, | |||||
| filename: record.name, | |||||
| }); | |||||
| }, [documentId, downloadFile, record.name]); | |||||
| const handleShowFileRenameModal = useCallback(() => { | |||||
| showFileRenameModal(record); | |||||
| }, [record, showFileRenameModal]); | |||||
| return ( | return ( | ||||
| <section className="flex gap-4 items-center"> | <section className="flex gap-4 items-center"> | ||||
| <Button | <Button | ||||
| </Button> | </Button> | ||||
| </DropdownMenuTrigger> | </DropdownMenuTrigger> | ||||
| <DropdownMenuContent align="end"> | <DropdownMenuContent align="end"> | ||||
| <DropdownMenuLabel>Actions</DropdownMenuLabel> | |||||
| <DropdownMenuItem | <DropdownMenuItem | ||||
| onClick={() => navigator.clipboard.writeText(record.id)} | onClick={() => navigator.clipboard.writeText(record.id)} | ||||
| > | > | ||||
| Copy payment ID | |||||
| {t('common.move')} | |||||
| </DropdownMenuItem> | </DropdownMenuItem> | ||||
| <DropdownMenuSeparator /> | <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> | </DropdownMenuContent> | ||||
| </DropdownMenu> | </DropdownMenu> | ||||
| </section> | </section> |
| import { ArrowUpDown } from 'lucide-react'; | import { ArrowUpDown } from 'lucide-react'; | ||||
| import * as React from 'react'; | import * as React from 'react'; | ||||
| import { RenameDialog } from '@/components/rename-dialog'; | |||||
| import SvgIcon from '@/components/svg-icon'; | import SvgIcon from '@/components/svg-icon'; | ||||
| import { TableEmpty, TableSkeleton } from '@/components/table-skeleton'; | import { TableEmpty, TableSkeleton } from '@/components/table-skeleton'; | ||||
| import { Badge } from '@/components/ui/badge'; | import { Badge } from '@/components/ui/badge'; | ||||
| 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 { useHandleConnectToKnowledge, useNavigateToOtherFolder } from './hooks'; | |||||
| import { | |||||
| useHandleConnectToKnowledge, | |||||
| useNavigateToOtherFolder, | |||||
| useRenameCurrentFile, | |||||
| } from './hooks'; | |||||
| import { LinkToDatasetDialog } from './link-to-dataset-dialog'; | import { LinkToDatasetDialog } from './link-to-dataset-dialog'; | ||||
| export function FilesTable() { | export function FilesTable() { | ||||
| onConnectToKnowledgeOk, | onConnectToKnowledgeOk, | ||||
| connectToKnowledgeLoading, | connectToKnowledgeLoading, | ||||
| } = useHandleConnectToKnowledge(); | } = useHandleConnectToKnowledge(); | ||||
| const { | |||||
| fileRenameVisible, | |||||
| showFileRenameModal, | |||||
| hideFileRenameModal, | |||||
| onFileRenameOk, | |||||
| initialFileName, | |||||
| fileRenameLoading, | |||||
| } = useRenameCurrentFile(); | |||||
| const { pagination, data, loading, setPagination } = useFetchFileList(); | const { pagination, data, loading, setPagination } = useFetchFileList(); | ||||
| <ActionCell | <ActionCell | ||||
| row={row} | row={row} | ||||
| showConnectToKnowledgeModal={showConnectToKnowledgeModal} | showConnectToKnowledgeModal={showConnectToKnowledgeModal} | ||||
| showFileRenameModal={showFileRenameModal} | |||||
| ></ActionCell> | ></ActionCell> | ||||
| ); | ); | ||||
| }, | }, | ||||
| loading={connectToKnowledgeLoading} | loading={connectToKnowledgeLoading} | ||||
| ></LinkToDatasetDialog> | ></LinkToDatasetDialog> | ||||
| )} | )} | ||||
| {fileRenameVisible && ( | |||||
| <RenameDialog | |||||
| hideModal={hideFileRenameModal} | |||||
| onOk={onFileRenameOk} | |||||
| initialName={initialFileName} | |||||
| loading={fileRenameLoading} | |||||
| ></RenameDialog> | |||||
| )} | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } |
| }; | }; | ||||
| }; | }; | ||||
| export type UseRenameCurrentFileReturnType = ReturnType< | |||||
| typeof useRenameCurrentFile | |||||
| >; | |||||
| export const useSelectBreadcrumbItems = () => { | export const useSelectBreadcrumbItems = () => { | ||||
| const parentFolderList = useFetchParentFolderList(); | const parentFolderList = useFetchParentFolderList(); | ||||