| import React, { useEffect } from 'react'; | |
| import { useForm, Controller } from 'react-hook-form'; | |
| import { Button, Input, Label, OGDialog, OGDialogTemplate } from '@librechat/client'; | |
| import type { ConfigFieldDetail } from '~/common'; | |
| import { useLocalize } from '~/hooks'; | |
| interface MCPConfigDialogProps { | |
| isOpen: boolean; | |
| onOpenChange: (isOpen: boolean) => void; | |
| fieldsSchema: Record<string, ConfigFieldDetail>; | |
| initialValues: Record<string, string>; | |
| onSave: (updatedValues: Record<string, string>) => void; | |
| isSubmitting?: boolean; | |
| onRevoke?: () => void; | |
| serverName: string; | |
| } | |
| export default function MCPConfigDialog({ | |
| isOpen, | |
| onOpenChange, | |
| fieldsSchema, | |
| initialValues, | |
| onSave, | |
| isSubmitting = false, | |
| onRevoke, | |
| serverName, | |
| }: MCPConfigDialogProps) { | |
| const localize = useLocalize(); | |
| const { | |
| control, | |
| handleSubmit, | |
| reset, | |
| formState: { errors }, | |
| } = useForm<Record<string, string>>({ | |
| defaultValues: initialValues, | |
| }); | |
| useEffect(() => { | |
| if (isOpen) { | |
| reset(initialValues); | |
| } | |
| }, [isOpen, initialValues, reset]); | |
| const onFormSubmit = (data: Record<string, string>) => { | |
| onSave(data); | |
| }; | |
| const handleRevoke = () => { | |
| if (onRevoke) { | |
| onRevoke(); | |
| } | |
| }; | |
| const dialogTitle = localize('com_ui_configure_mcp_variables_for', { 0: serverName }); | |
| return ( | |
| <OGDialog open={isOpen} onOpenChange={onOpenChange}> | |
| <OGDialogTemplate | |
| className="sm:max-w-lg" | |
| title={dialogTitle} | |
| headerClassName="px-6 pt-6 pb-4" | |
| main={ | |
| <form onSubmit={handleSubmit(onFormSubmit)} className="space-y-4 px-6 pb-2"> | |
| {Object.entries(fieldsSchema).map(([key, details]) => ( | |
| <div key={key} className="space-y-2"> | |
| <Label htmlFor={key} className="text-sm font-medium"> | |
| {details.title} | |
| </Label> | |
| <Controller | |
| name={key} | |
| control={control} | |
| defaultValue={initialValues[key] || ''} | |
| render={({ field }) => ( | |
| <Input | |
| id={key} | |
| type="text" | |
| {...field} | |
| placeholder={localize('com_ui_mcp_enter_var', { 0: details.title })} | |
| className="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white sm:text-sm" | |
| /> | |
| )} | |
| /> | |
| {details.description && ( | |
| <p | |
| className="text-xs text-text-secondary [&_a]:text-blue-500 [&_a]:hover:text-blue-600 dark:[&_a]:text-blue-400 dark:[&_a]:hover:text-blue-300" | |
| dangerouslySetInnerHTML={{ __html: details.description }} | |
| /> | |
| )} | |
| {errors[key] && <p className="text-xs text-red-500">{errors[key]?.message}</p>} | |
| </div> | |
| ))} | |
| </form> | |
| } | |
| selection={{ | |
| selectHandler: handleSubmit(onFormSubmit), | |
| selectClasses: 'bg-green-500 hover:bg-green-600 text-white', | |
| selectText: isSubmitting ? localize('com_ui_saving') : localize('com_ui_save'), | |
| }} | |
| buttons={ | |
| onRevoke && ( | |
| <Button | |
| onClick={handleRevoke} | |
| className="bg-red-600 text-white hover:bg-red-700 dark:hover:bg-red-800" | |
| disabled={isSubmitting} | |
| > | |
| {localize('com_ui_revoke')} | |
| </Button> | |
| ) | |
| } | |
| footerClassName="flex justify-end gap-2 px-6 pb-6 pt-2" | |
| showCancelButton={true} | |
| /> | |
| </OGDialog> | |
| ); | |
| } | |