Feat: Render agent setting dialog #3221 (#9312)

### What problem does this PR solve?

Feat: Render agent setting dialog #3221
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-08-08 11:00:55 +08:00
committed by GitHub
parent 1bd64dafcb
commit 58a64000ea
10 changed files with 339 additions and 25 deletions

View File

@ -27,6 +27,7 @@ import {
LaptopMinimalCheck,
Logs,
ScreenShare,
Settings,
Upload,
} from 'lucide-react';
import { ComponentPropsWithoutRef, useCallback } from 'react';
@ -43,6 +44,7 @@ import {
useWatchAgentChange,
} from './hooks/use-save-graph';
import { useShowEmbedModal } from './hooks/use-show-dialog';
import { SettingDialog } from './setting-dialog';
import { UploadAgentDialog } from './upload-agent-dialog';
import { useAgentHistoryManager } from './use-agent-history-manager';
import { VersionDialog } from './version-dialog';
@ -92,6 +94,12 @@ export default function Agent() {
showModal: showVersionDialog,
} = useSetModalState();
const {
visible: settingDialogVisible,
hideModal: hideSettingDialog,
showModal: showSettingDialog,
} = useSetModalState();
const { showEmbedModal, hideEmbedModal, embedVisible, beta } =
useShowEmbedModal();
const { navigateToAgentLogs } = useNavigatePage();
@ -149,11 +157,6 @@ export default function Agent() {
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{/* <AgentDropdownMenuItem onClick={openDocument}>
<Key />
API
</AgentDropdownMenuItem> */}
{/* <DropdownMenuSeparator /> */}
<AgentDropdownMenuItem onClick={handleImportJson}>
<Download />
{t('flow.import')}
@ -163,6 +166,11 @@ export default function Agent() {
<Upload />
{t('flow.export')}
</AgentDropdownMenuItem>
<DropdownMenuSeparator />
<AgentDropdownMenuItem onClick={showSettingDialog}>
<Settings />
{t('flow.setting')}
</AgentDropdownMenuItem>
{location.hostname !== 'demo.ragflow.io' && (
<>
<DropdownMenuSeparator />
@ -201,6 +209,9 @@ export default function Agent() {
{versionDialogVisible && (
<VersionDialog hideModal={hideVersionDialog}></VersionDialog>
)}
{settingDialogVisible && (
<SettingDialog hideModal={hideSettingDialog}></SettingDialog>
)}
</section>
);
}

View File

@ -0,0 +1,53 @@
import { ButtonLoading } from '@/components/ui/button';
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { useSetAgentSetting } from '@/hooks/use-agent-request';
import { IModalProps } from '@/interfaces/common';
import { transformFile2Base64 } from '@/utils/file-util';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import {
AgentSettingId,
SettingForm,
SettingFormSchemaType,
} from './setting-form';
export function SettingDialog({ hideModal }: IModalProps<any>) {
const { t } = useTranslation();
const { setAgentSetting } = useSetAgentSetting();
const submit = useCallback(
async (values: SettingFormSchemaType) => {
const avatar = values.avatar;
const code = await setAgentSetting({
...values,
avatar: avatar.length > 0 ? await transformFile2Base64(avatar[0]) : '',
});
if (code === 0) {
hideModal?.();
}
},
[hideModal, setAgentSetting],
);
return (
<Dialog open onOpenChange={hideModal}>
<DialogContent>
<DialogHeader>
<DialogTitle>Are you absolutely sure?</DialogTitle>
</DialogHeader>
<SettingForm submit={submit}></SettingForm>
<DialogFooter>
<ButtonLoading type="submit" form={AgentSettingId} loading={false}>
{t('common.save')}
</ButtonLoading>
</DialogFooter>
</DialogContent>
</Dialog>
);
}

View File

@ -0,0 +1,158 @@
import { z } from 'zod';
import {
FileUpload,
FileUploadDropzone,
FileUploadItem,
FileUploadItemDelete,
FileUploadItemMetadata,
FileUploadItemPreview,
FileUploadList,
FileUploadTrigger,
} from '@/components/file-upload';
import { RAGFlowFormItem } from '@/components/ragflow-form';
import { Button } from '@/components/ui/button';
import { Form, FormControl, FormItem, FormLabel } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Textarea } from '@/components/ui/textarea';
import { useTranslate } from '@/hooks/common-hooks';
import { useFetchAgent } from '@/hooks/use-agent-request';
import { transformBase64ToFile } from '@/utils/file-util';
import { zodResolver } from '@hookform/resolvers/zod';
import { CloudUpload, X } from 'lucide-react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
const formSchema = z.object({
title: z.string().min(1, {}),
avatar: z.array(z.custom<File>()),
description: z.string(),
permission: z.string(),
});
export type SettingFormSchemaType = z.infer<typeof formSchema>;
export const AgentSettingId = 'agentSettingId';
type SettingFormProps = {
submit: (values: SettingFormSchemaType) => void;
};
export function SettingForm({ submit }: SettingFormProps) {
const { t } = useTranslate('flow.settings');
const { data } = useFetchAgent();
const form = useForm<SettingFormSchemaType>({
resolver: zodResolver(formSchema),
defaultValues: {
title: '',
permission: 'me',
},
});
useEffect(() => {
form.reset({
title: data?.title,
description: data?.description,
avatar: data.avatar ? [transformBase64ToFile(data.avatar)] : [],
permission: data?.permission,
});
}, [data, form]);
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(submit)}
className="space-y-8"
id={AgentSettingId}
>
<RAGFlowFormItem name="title" label={t('title')}>
<Input />
</RAGFlowFormItem>
<RAGFlowFormItem name="avatar" label={t('photo')}>
{(field) => (
<FileUpload
value={field.value}
onValueChange={field.onChange}
accept="image/*"
maxFiles={1}
onFileReject={(_, message) => {
form.setError('avatar', {
message,
});
}}
multiple
>
<FileUploadDropzone className="flex-row flex-wrap border-dotted text-center">
<CloudUpload className="size-4" />
Drag and drop or
<FileUploadTrigger asChild>
<Button variant="link" size="sm" className="p-0">
choose files
</Button>
</FileUploadTrigger>
to upload
</FileUploadDropzone>
<FileUploadList>
{field.value?.map((file: File, index: number) => (
<FileUploadItem key={index} value={file}>
<FileUploadItemPreview />
<FileUploadItemMetadata />
<FileUploadItemDelete asChild>
<Button variant="ghost" size="icon" className="size-7">
<X />
<span className="sr-only">Delete</span>
</Button>
</FileUploadItemDelete>
</FileUploadItem>
))}
</FileUploadList>
</FileUpload>
)}
</RAGFlowFormItem>
<RAGFlowFormItem name="description" label={t('description')}>
<Textarea rows={4} />
</RAGFlowFormItem>
<RAGFlowFormItem
name="permission"
label={t('permissions')}
tooltip={t('permissionsTip')}
>
{(field) => (
<RadioGroup
onValueChange={field.onChange}
value={field.value}
className="flex"
>
<FormItem className="flex items-center gap-3">
<FormControl>
<RadioGroupItem value="me" id="me" />
</FormControl>
<FormLabel
className="font-normal !m-0 cursor-pointer"
htmlFor="me"
>
{t('me')}
</FormLabel>
</FormItem>
<FormItem className="flex items-center gap-3">
<FormControl>
<RadioGroupItem value="team" id="team" />
</FormControl>
<FormLabel
className="font-normal !m-0 cursor-pointer"
htmlFor="team"
>
{t('team')}
</FormLabel>
</FormItem>
</RadioGroup>
)}
</RAGFlowFormItem>
</form>
</Form>
);
}

View File

@ -1,5 +1,6 @@
import { MoreButton } from '@/components/more-button';
import { RAGFlowAvatar } from '@/components/ragflow-avatar';
import { SharedBadge } from '@/components/shared-badge';
import { Card, CardContent } from '@/components/ui/card';
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
import { IFlow } from '@/interfaces/database/flow';
@ -24,6 +25,7 @@ export function AgentCard({ data, showAgentRenameModal }: DatasetCardProps) {
avatar={data.avatar}
name={data.title || 'CN'}
></RAGFlowAvatar>
<SharedBadge>{data.nickname}</SharedBadge>
</div>
<AgentDropdown
showAgentRenameModal={showAgentRenameModal}