import { useEffect, useState } from 'react' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { useQueryClient } from '@tanstack/react-query' import { Loader2 } from 'lucide-react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form' import { Input } from '@/components/ui/input' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Separator } from '@/components/ui/separator' import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, } from '@/components/ui/sheet' import { Textarea } from '@/components/ui/textarea' import { JsonEditor } from '@/components/json-editor' import { StatusBadge } from '@/components/status-badge' import { TagInput } from '@/components/tag-input' import { createPrefillGroup, updatePrefillGroup } from '../../api' import { ENDPOINT_TEMPLATES } from '../../constants' import { prefillGroupsQueryKeys } from '../../lib' import { prefillGroupFormSchema, type PrefillGroup, type PrefillGroupFormValues, } from '../../types' import { DEFAULT_FORM_VALUES, PREFILL_GROUP_TYPE_META, PREFILL_GROUP_TYPES, type PrefillGroupType, parseStringItems, serializeEndpointItems, } from '../prefill-group-shared' type PrefillGroupFormDrawerProps = { open: boolean onClose: () => void currentGroup: PrefillGroup | null } export function PrefillGroupFormDrawer({ open, onClose, currentGroup, }: PrefillGroupFormDrawerProps) { const { t } = useTranslation() const queryClient = useQueryClient() const isEdit = Boolean(currentGroup?.id) const [isSaving, setIsSaving] = useState(false) const form = useForm({ resolver: zodResolver(prefillGroupFormSchema), defaultValues: DEFAULT_FORM_VALUES, }) const selectedType = form.watch('type') useEffect(() => { if (open) { if (isEdit && currentGroup) { form.reset({ id: currentGroup.id, name: currentGroup.name, description: currentGroup.description || '', type: currentGroup.type, items: currentGroup.type === 'endpoint' ? serializeEndpointItems(currentGroup.items) : parseStringItems(currentGroup.items), }) } else { form.reset(DEFAULT_FORM_VALUES) } } }, [open, isEdit, currentGroup, form]) useEffect(() => { const currentItems = form.getValues('items') if (selectedType === 'endpoint' && Array.isArray(currentItems)) { form.setValue('items', '', { shouldValidate: false }) } else if ( selectedType !== 'endpoint' && typeof currentItems === 'string' ) { form.setValue('items', [], { shouldValidate: false }) } }, [selectedType, form]) const handleOpenChange = (nextOpen: boolean) => { if (!nextOpen) { onClose() } } const handleSubmit = async (values: PrefillGroupFormValues) => { setIsSaving(true) const payload = { name: values.name.trim(), type: values.type, description: values.description?.trim() || '', items: values.type === 'endpoint' ? typeof values.items === 'string' ? values.items : '' : Array.isArray(values.items) ? values.items : [], } try { const response = isEdit ? await updatePrefillGroup({ id: currentGroup!.id, ...payload, }) : await createPrefillGroup(payload) if (response.success) { toast.success( isEdit ? 'Prefill group updated' : 'Prefill group created' ) queryClient.invalidateQueries({ queryKey: prefillGroupsQueryKeys.lists(), }) onClose() } else { toast.error(response.message || 'Operation failed') } } catch (err: unknown) { toast.error((err as Error)?.message || 'Operation failed') } finally { setIsSaving(false) } } const meta = PREFILL_GROUP_TYPE_META[selectedType] || PREFILL_GROUP_TYPE_META.model return ( {isEdit ? t('Edit Prefill Group') : t('Create Prefill Group')} {isEdit ? t('Update the reusable bundle below.') : t('Capture a reusable bundle of models, tags, or endpoints.')}

{t('Group details')}

{t( 'Give the group a recognizable name and optional description.' )}

( {t('Group Name')} {t('Give this group a recognizable name.')} )} /> ( {t('Description')}