'use client' import type { AppIconSelection } from '@/app/components/base/app-icon-picker' import type { SnippetDetail } from '@/models/snippet' import { AlertDialog, AlertDialogActions, AlertDialogCancelButton, AlertDialogConfirmButton, AlertDialogContent, AlertDialogDescription, AlertDialogTitle, } from '@langgenius/dify-ui/alert-dialog' import { cn } from '@langgenius/dify-ui/cn' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@langgenius/dify-ui/dropdown-menu' import { toast } from '@langgenius/dify-ui/toast' import * as React from 'react' import { useTranslation } from 'react-i18next' import CreateSnippetDialog from '@/app/components/workflow/create-snippet-dialog' import { useRouter } from '@/next/navigation' import { useDeleteSnippetMutation, useExportSnippetMutation, useUpdateSnippetMutation } from '@/service/use-snippets' import { downloadBlob } from '@/utils/download' type SnippetInfoDropdownProps = { snippet: SnippetDetail } const FALLBACK_ICON: AppIconSelection = { type: 'emoji', icon: '🤖', background: '#FFEAD5', } const SnippetInfoDropdown = ({ snippet }: SnippetInfoDropdownProps) => { const { t } = useTranslation('snippet') const { replace } = useRouter() const [open, setOpen] = React.useState(false) const [isEditDialogOpen, setIsEditDialogOpen] = React.useState(false) const [isDeleteDialogOpen, setIsDeleteDialogOpen] = React.useState(false) const updateSnippetMutation = useUpdateSnippetMutation() const exportSnippetMutation = useExportSnippetMutation() const deleteSnippetMutation = useDeleteSnippetMutation() const initialValue = React.useMemo(() => ({ name: snippet.name, description: snippet.description, icon: snippet.icon ? { type: 'emoji' as const, icon: snippet.icon, background: snippet.iconBackground || FALLBACK_ICON.background, } : FALLBACK_ICON, }), [snippet.description, snippet.icon, snippet.iconBackground, snippet.name]) const handleOpenEditDialog = React.useCallback(() => { setOpen(false) setIsEditDialogOpen(true) }, []) const handleExportSnippet = React.useCallback(async () => { setOpen(false) try { const data = await exportSnippetMutation.mutateAsync({ snippetId: snippet.id }) const file = new Blob([data], { type: 'application/yaml' }) downloadBlob({ data: file, fileName: `${snippet.name}.yml` }) } catch { toast.error(t('exportFailed')) } }, [exportSnippetMutation, snippet.id, snippet.name, t]) const handleEditSnippet = React.useCallback(async ({ name, description, icon }: { name: string description: string icon: AppIconSelection }) => { updateSnippetMutation.mutate({ params: { snippetId: snippet.id }, body: { name, description: description || undefined, icon_info: { icon: icon.type === 'emoji' ? icon.icon : icon.fileId, icon_type: icon.type, icon_background: icon.type === 'emoji' ? icon.background : undefined, icon_url: icon.type === 'image' ? icon.url : undefined, }, }, }, { onSuccess: () => { toast.success(t('editDone')) setIsEditDialogOpen(false) }, onError: (error) => { toast.error(error instanceof Error ? error.message : t('editFailed')) }, }) }, [snippet.id, t, updateSnippetMutation]) const handleDeleteSnippet = React.useCallback(() => { deleteSnippetMutation.mutate({ params: { snippetId: snippet.id }, }, { onSuccess: () => { toast.success(t('deleted')) setIsDeleteDialogOpen(false) replace('/snippets') }, onError: (error) => { toast.error(error instanceof Error ? error.message : t('deleteFailed')) }, }) }, [deleteSnippetMutation, replace, snippet.id, t]) return ( <> {t('menu.editInfo')} {t('menu.exportSnippet')} { setOpen(false) setIsDeleteDialogOpen(true) }} > {t('menu.deleteSnippet')} {isEditDialogOpen && ( setIsEditDialogOpen(false)} onConfirm={handleEditSnippet} /> )}
{t('deleteConfirmTitle')} {t('deleteConfirmContent')}
{t('operation.cancel', { ns: 'common' })} {t('menu.deleteSnippet')}
) } export default React.memo(SnippetInfoDropdown)