import { useState, useEffect } from 'react'
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
} from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { IconPlus, IconTrash, IconGripVertical } from '@tabler/icons-react'
import { MCPServerConfig } from '@/hooks/useMCPServers'
import { useTranslation } from '@/i18n/react-i18next-compat'
import {
DndContext,
closestCenter,
useSensor,
useSensors,
PointerSensor,
KeyboardSensor,
} from '@dnd-kit/core'
import {
SortableContext,
verticalListSortingStrategy,
arrayMove,
useSortable,
} from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
import { cn } from '@/lib/utils'
// Sortable argument item component
function SortableArgItem({
id,
value,
onChange,
onRemove,
canRemove,
placeholder,
}: {
id: number
value: string
onChange: (value: string) => void
onRemove: () => void
canRemove: boolean
placeholder: string
}) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({ id })
const style = {
transform: CSS.Transform.toString(transform),
transition,
opacity: isDragging ? 0.5 : 1,
}
return (
onChange(e.target.value)}
placeholder={placeholder}
className="flex-1"
/>
{canRemove && (
)}
)
}
interface AddEditMCPServerProps {
open: boolean
onOpenChange: (open: boolean) => void
editingKey: string | null
initialData?: MCPServerConfig
onSave: (name: string, config: MCPServerConfig) => void
}
export default function AddEditMCPServer({
open,
onOpenChange,
editingKey,
initialData,
onSave,
}: AddEditMCPServerProps) {
const { t } = useTranslation()
const [serverName, setServerName] = useState('')
const [command, setCommand] = useState('')
const [args, setArgs] = useState([''])
const [envKeys, setEnvKeys] = useState([''])
const [envValues, setEnvValues] = useState([''])
// Reset form when modal opens/closes or editing key changes
useEffect(() => {
if (open && editingKey && initialData) {
setServerName(editingKey)
setCommand(initialData.command)
setArgs(initialData.args?.length > 0 ? initialData.args : [''])
if (initialData.env) {
// Convert env object to arrays of keys and values
const keys = Object.keys(initialData.env)
const values = keys.map((key) => initialData.env[key])
setEnvKeys(keys.length > 0 ? keys : [''])
setEnvValues(values.length > 0 ? values : [''])
}
} else if (open) {
// Add mode - reset form
resetForm()
}
}, [open, editingKey, initialData])
const resetForm = () => {
setServerName('')
setCommand('')
setArgs([''])
setEnvKeys([''])
setEnvValues([''])
}
const handleAddArg = () => {
setArgs([...args, ''])
}
const handleRemoveArg = (index: number) => {
const newArgs = [...args]
newArgs.splice(index, 1)
setArgs(newArgs.length > 0 ? newArgs : [''])
}
const handleArgChange = (index: number, value: string) => {
const newArgs = [...args]
newArgs[index] = value
setArgs(newArgs)
}
const handleReorderArgs = (oldIndex: number, newIndex: number) => {
setArgs(arrayMove(args, oldIndex, newIndex))
}
// Sensors for drag and drop
const sensors = useSensors(
useSensor(PointerSensor, {
activationConstraint: {
delay: 100,
tolerance: 5,
},
}),
useSensor(KeyboardSensor)
)
const handleAddEnv = () => {
setEnvKeys([...envKeys, ''])
setEnvValues([...envValues, ''])
}
const handleRemoveEnv = (index: number) => {
const newKeys = [...envKeys]
const newValues = [...envValues]
newKeys.splice(index, 1)
newValues.splice(index, 1)
setEnvKeys(newKeys.length > 0 ? newKeys : [''])
setEnvValues(newValues.length > 0 ? newValues : [''])
}
const handleEnvKeyChange = (index: number, value: string) => {
const newKeys = [...envKeys]
newKeys[index] = value
setEnvKeys(newKeys)
}
const handleEnvValueChange = (index: number, value: string) => {
const newValues = [...envValues]
newValues[index] = value
setEnvValues(newValues)
}
const handleSave = () => {
// Convert env arrays to object
const envObj: Record = {}
envKeys.forEach((key, index) => {
const keyName = key.trim()
if (keyName !== '') {
envObj[keyName] = envValues[index]?.trim() || ''
}
})
// Filter out empty args
const filteredArgs = args.map((arg) => arg.trim()).filter((arg) => arg)
const config: MCPServerConfig = {
command: command.trim(),
args: filteredArgs,
env: envObj,
}
if (serverName.trim() !== '') {
onSave(serverName.trim(), config)
onOpenChange(false)
resetForm()
}
}
return (
)
}