Feat: Rename a dataset #3221 (#7162)

### What problem does this PR solve?

Feat: Rename a dataset #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu 2025-04-22 10:09:41 +08:00 committed by GitHub
parent ad220a0a3c
commit e7f83b13ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 151 additions and 20 deletions

View File

@ -78,13 +78,15 @@ const DropdownMenuItem = React.forwardRef<
React.ElementRef<typeof DropdownMenuPrimitive.Item>, React.ElementRef<typeof DropdownMenuPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & { React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean; inset?: boolean;
justifyBetween?: boolean;
} }
>(({ className, inset, ...props }, ref) => ( >(({ className, inset, justifyBetween = true, ...props }, ref) => (
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
ref={ref} ref={ref}
className={cn( className={cn(
'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0', 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
inset && 'pl-8', inset && 'pl-8',
justifyBetween && 'flex justify-between',
className, className,
)} )}
{...props} {...props}

View File

@ -180,7 +180,7 @@ export const useDeleteKnowledge = () => {
//#region knowledge configuration //#region knowledge configuration
export const useUpdateKnowledge = () => { export const useUpdateKnowledge = (shouldFetchList = false) => {
const knowledgeBaseId = useKnowledgeBaseId(); const knowledgeBaseId = useKnowledgeBaseId();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { const {
@ -191,12 +191,18 @@ export const useUpdateKnowledge = () => {
mutationKey: ['saveKnowledge'], mutationKey: ['saveKnowledge'],
mutationFn: async (params: Record<string, any>) => { mutationFn: async (params: Record<string, any>) => {
const { data = {} } = await kbService.updateKb({ const { data = {} } = await kbService.updateKb({
kb_id: knowledgeBaseId, kb_id: params?.kb_id ? params?.kb_id : knowledgeBaseId,
...params, ...params,
}); });
if (data.code === 0) { if (data.code === 0) {
message.success(i18n.t(`message.updated`)); message.success(i18n.t(`message.updated`));
queryClient.invalidateQueries({ queryKey: ['fetchKnowledgeDetail'] }); if (shouldFetchList) {
queryClient.invalidateQueries({
queryKey: ['infiniteFetchKnowledgeList'],
});
} else {
queryClient.invalidateQueries({ queryKey: ['fetchKnowledgeDetail'] });
}
} }
return data; return data;
}, },

View File

@ -23,7 +23,7 @@ import { z } from 'zod';
const FormId = 'dataset-creating-form'; const FormId = 'dataset-creating-form';
export function InputForm() { export function InputForm({ onOk }: IModalProps<any>) {
const { t } = useTranslation(); const { t } = useTranslation();
const FormSchema = z.object({ const FormSchema = z.object({
@ -43,7 +43,7 @@ export function InputForm() {
}); });
function onSubmit(data: z.infer<typeof FormSchema>) { function onSubmit(data: z.infer<typeof FormSchema>) {
console.log('🚀 ~ onSubmit ~ data:', data); onOk?.(data.name);
} }
return ( return (
@ -74,7 +74,7 @@ export function InputForm() {
); );
} }
export function DatasetCreatingDialog({ hideModal }: IModalProps<any>) { export function DatasetCreatingDialog({ hideModal, onOk }: IModalProps<any>) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
@ -83,7 +83,7 @@ export function DatasetCreatingDialog({ hideModal }: IModalProps<any>) {
<DialogHeader> <DialogHeader>
<DialogTitle>{t('knowledgeList.createKnowledgeBase')}</DialogTitle> <DialogTitle>{t('knowledgeList.createKnowledgeBase')}</DialogTitle>
</DialogHeader> </DialogHeader>
<InputForm></InputForm> <InputForm onOk={onOk}></InputForm>
<DialogFooter> <DialogFooter>
<Button type="submit" variant={'tertiary'} size={'sm'} form={FormId}> <Button type="submit" variant={'tertiary'} size={'sm'} form={FormId}>
{t('common.save')} {t('common.save')}

View File

@ -0,0 +1,54 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useDeleteKnowledge } from '@/hooks/knowledge-hooks';
import { IKnowledge } from '@/interfaces/database/knowledge';
import { PenLine, Trash2 } from 'lucide-react';
import { PropsWithChildren, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useRenameDataset } from './use-rename-dataset';
export function DatasetDropdown({
children,
showDatasetRenameModal,
dataset,
}: PropsWithChildren &
Pick<ReturnType<typeof useRenameDataset>, 'showDatasetRenameModal'> & {
dataset: IKnowledge;
}) {
const { t } = useTranslation();
const { deleteKnowledge } = useDeleteKnowledge();
const handleShowDatasetRenameModal = useCallback(() => {
showDatasetRenameModal(dataset);
}, [dataset, showDatasetRenameModal]);
const handleDelete = useCallback(() => {
deleteKnowledge(dataset.id);
}, [dataset.id, deleteKnowledge]);
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={handleShowDatasetRenameModal}>
{t('common.rename')} <PenLine />
</DropdownMenuItem>
<DropdownMenuSeparator />
<ConfirmDeleteDialog onOk={handleDelete}>
<DropdownMenuItem
className="text-text-delete-red"
onSelect={(e) => e.preventDefault()}
>
{t('common.delete')} <Trash2 />
</DropdownMenuItem>
</ConfirmDeleteDialog>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -1,8 +1,7 @@
import { KnowledgeRouteKey } from '@/constants/knowledge';
import { useSetModalState } from '@/hooks/common-hooks'; import { useSetModalState } from '@/hooks/common-hooks';
import { useCreateKnowledge } from '@/hooks/knowledge-hooks'; import { useCreateKnowledge } from '@/hooks/knowledge-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useNavigate } from 'umi';
export const useSearchKnowledge = () => { export const useSearchKnowledge = () => {
const [searchString, setSearchString] = useState<string>(''); const [searchString, setSearchString] = useState<string>('');
@ -19,7 +18,7 @@ export const useSearchKnowledge = () => {
export const useSaveKnowledge = () => { export const useSaveKnowledge = () => {
const { visible: visible, hideModal, showModal } = useSetModalState(); const { visible: visible, hideModal, showModal } = useSetModalState();
const { loading, createKnowledge } = useCreateKnowledge(); const { loading, createKnowledge } = useCreateKnowledge();
const navigate = useNavigate(); const { navigateToDataset } = useNavigatePage();
const onCreateOk = useCallback( const onCreateOk = useCallback(
async (name: string) => { async (name: string) => {
@ -29,12 +28,10 @@ export const useSaveKnowledge = () => {
if (ret?.code === 0) { if (ret?.code === 0) {
hideModal(); hideModal();
navigate( navigateToDataset(ret.data.kb_id)();
`/knowledge/${KnowledgeRouteKey.Configuration}?id=${ret.data.kb_id}`,
);
} }
}, },
[createKnowledge, hideModal, navigate], [createKnowledge, hideModal, navigateToDataset],
); );
return { return {

View File

@ -1,5 +1,5 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import ListFilterBar from '@/components/list-filter-bar'; import ListFilterBar from '@/components/list-filter-bar';
import { RenameDialog } from '@/components/rename-dialog';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card'; import { Card, CardContent } from '@/components/ui/card';
@ -7,10 +7,12 @@ import { useInfiniteFetchKnowledgeList } from '@/hooks/knowledge-hooks';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IKnowledge } from '@/interfaces/database/knowledge'; import { IKnowledge } from '@/interfaces/database/knowledge';
import { formatDate } from '@/utils/date'; import { formatDate } from '@/utils/date';
import { ChevronRight, Plus, Trash2 } from 'lucide-react'; import { ChevronRight, Ellipsis, Plus } from 'lucide-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { DatasetCreatingDialog } from './dataset-creating-dialog'; import { DatasetCreatingDialog } from './dataset-creating-dialog';
import { DatasetDropdown } from './dataset-dropdown';
import { useSaveKnowledge } from './hooks'; import { useSaveKnowledge } from './hooks';
import { useRenameDataset } from './use-rename-dataset';
export default function Datasets() { export default function Datasets() {
const { const {
@ -41,6 +43,15 @@ export default function Datasets() {
return data?.pages.at(-1).total ?? 0; return data?.pages.at(-1).total ?? 0;
}, [data?.pages]); }, [data?.pages]);
const {
datasetRenameLoading,
initialDatasetName,
onDatasetRenameOk,
datasetRenameVisible,
hideDatasetRenameModal,
showDatasetRenameModal,
} = useRenameDataset();
return ( return (
<section className="p-8 text-foreground"> <section className="p-8 text-foreground">
<ListFilterBar title="Datasets" showDialog={showModal}> <ListFilterBar title="Datasets" showDialog={showModal}>
@ -59,11 +70,14 @@ export default function Datasets() {
<AvatarImage src={dataset.avatar} /> <AvatarImage src={dataset.avatar} />
<AvatarFallback className="rounded-lg">CN</AvatarFallback> <AvatarFallback className="rounded-lg">CN</AvatarFallback>
</Avatar> </Avatar>
<ConfirmDeleteDialog> <DatasetDropdown
showDatasetRenameModal={showDatasetRenameModal}
dataset={dataset}
>
<Button variant="ghost" size="icon"> <Button variant="ghost" size="icon">
<Trash2 /> <Ellipsis />
</Button> </Button>
</ConfirmDeleteDialog> </DatasetDropdown>
</div> </div>
<div className="flex justify-between items-end"> <div className="flex justify-between items-end">
<div> <div>
@ -92,6 +106,14 @@ export default function Datasets() {
loading={creatingLoading} loading={creatingLoading}
></DatasetCreatingDialog> ></DatasetCreatingDialog>
)} )}
{datasetRenameVisible && (
<RenameDialog
hideModal={hideDatasetRenameModal}
onOk={onDatasetRenameOk}
initialName={initialDatasetName}
loading={datasetRenameLoading}
></RenameDialog>
)}
</section> </section>
); );
} }

View File

@ -0,0 +1,47 @@
import { useSetModalState } from '@/hooks/common-hooks';
import { useUpdateKnowledge } from '@/hooks/knowledge-hooks';
import { IKnowledge } from '@/interfaces/database/knowledge';
import { omit } from 'lodash';
import { useCallback, useState } from 'react';
export const useRenameDataset = () => {
const [dataset, setDataset] = useState<IKnowledge>({} as IKnowledge);
const {
visible: datasetRenameVisible,
hideModal: hideDatasetRenameModal,
showModal: showDatasetRenameModal,
} = useSetModalState();
const { saveKnowledgeConfiguration, loading } = useUpdateKnowledge(true);
const onDatasetRenameOk = useCallback(
async (name: string) => {
const ret = await saveKnowledgeConfiguration({
...omit(dataset, ['id', 'update_time', 'nickname', 'tenant_avatar']),
kb_id: dataset.id,
name,
});
if (ret.code === 0) {
hideDatasetRenameModal();
}
},
[saveKnowledgeConfiguration, dataset, hideDatasetRenameModal],
);
const handleShowDatasetRenameModal = useCallback(
async (record: IKnowledge) => {
setDataset(record);
showDatasetRenameModal();
},
[showDatasetRenameModal],
);
return {
datasetRenameLoading: loading,
initialDatasetName: dataset?.name,
onDatasetRenameOk,
datasetRenameVisible,
hideDatasetRenameModal,
showDatasetRenameModal: handleShowDatasetRenameModal,
};
};

View File

@ -40,6 +40,7 @@ module.exports = {
'colors-text-inverse-strong': 'var(--colors-text-inverse-strong)', 'colors-text-inverse-strong': 'var(--colors-text-inverse-strong)',
'colors-text-persist-light': 'var(--colors-text-persist-light)', 'colors-text-persist-light': 'var(--colors-text-persist-light)',
'colors-text-inverse-weak': 'var(--colors-text-inverse-weak)', 'colors-text-inverse-weak': 'var(--colors-text-inverse-weak)',
'text-delete-red': 'var(--text-delete-red)',
primary: { primary: {
DEFAULT: 'hsl(var(--primary))', DEFAULT: 'hsl(var(--primary))',

View File

@ -61,6 +61,7 @@
--colors-text-inverse-strong: rgba(255, 255, 255, 1); --colors-text-inverse-strong: rgba(255, 255, 255, 1);
--colors-text-persist-light: rgba(255, 255, 255, 1); --colors-text-persist-light: rgba(255, 255, 255, 1);
--colors-text-inverse-weak: rgba(184, 181, 203, 1); --colors-text-inverse-weak: rgba(184, 181, 203, 1);
--text-delete-red: rgba(216, 73, 75, 1);
--sidebar-background: 0 0% 98%; --sidebar-background: 0 0% 98%;
--sidebar-foreground: 240 5.3% 26.1%; --sidebar-foreground: 240 5.3% 26.1%;
@ -153,6 +154,7 @@
--colors-text-inverse-strong: rgba(17, 16, 23, 1); --colors-text-inverse-strong: rgba(17, 16, 23, 1);
--colors-text-persist-light: rgba(255, 255, 255, 1); --colors-text-persist-light: rgba(255, 255, 255, 1);
--colors-text-inverse-weak: rgba(84, 80, 106, 1); --colors-text-inverse-weak: rgba(84, 80, 106, 1);
--text-delete-red: rgba(216, 73, 75, 1);
--sidebar-background: 240 5.9% 10%; --sidebar-background: 240 5.9% 10%;
--sidebar-foreground: 240 4.8% 95.9%; --sidebar-foreground: 240 4.8% 95.9%;