import type { Placement } from '@langgenius/dify-ui/dropdown-menu' import { cn } from '@langgenius/dify-ui/cn' import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger, } from '@langgenius/dify-ui/dropdown-menu' import { useCallback, useMemo, useState, } from 'react' import { useTranslation } from 'react-i18next' import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon' import PluginTaskList from './components/plugin-task-list' import TaskStatusIndicator from './components/task-status-indicator' import { usePluginTaskStatus } from './hooks' type PluginTasksProps = { animatedSlot?: boolean dropdownAnchor?: () => Element | null dropdownPlacement?: Placement } const PluginTasks = ({ animatedSlot = false, dropdownAnchor, dropdownPlacement = 'bottom', }: PluginTasksProps) => { const { t } = useTranslation() const [open, setOpen] = useState(false) const { errorPlugins, successPlugins, runningPlugins, runningPluginsLength, successPluginsLength, errorPluginsLength, totalPluginsLength, isInstalling, isInstallingWithSuccess, isInstallingWithError, isSuccess, isFailed, handleClearErrorPlugin, } = usePluginTaskStatus() const { getIconUrl } = useGetIcon() const hasPluginTasks = totalPluginsLength > 0 const canOpenMenu = isFailed || isInstalling || isInstallingWithSuccess || isInstallingWithError || isSuccess // Generate tooltip text based on status const tip = useMemo(() => { if (isInstallingWithError) return t('task.installingWithError', { ns: 'plugin', installingLength: runningPluginsLength, successLength: successPluginsLength, errorLength: errorPluginsLength }) if (isInstallingWithSuccess) return t('task.installingWithSuccess', { ns: 'plugin', installingLength: runningPluginsLength, successLength: successPluginsLength }) if (isInstalling) return t('task.installing', { ns: 'plugin' }) if (isFailed) return t('task.installedError', { ns: 'plugin', errorLength: errorPluginsLength }) if (isSuccess) return t('task.installSuccess', { ns: 'plugin', successLength: successPluginsLength }) return t('task.installed', { ns: 'plugin' }) }, [ errorPluginsLength, isFailed, isInstalling, isInstallingWithError, isInstallingWithSuccess, isSuccess, runningPluginsLength, successPluginsLength, t, ]) // Generic clear function that handles clearing and modal closing const clearPluginsAndClose = useCallback(async ( plugins: Array<{ taskId: string, plugin_unique_identifier: string }>, ) => { for (const plugin of plugins) await handleClearErrorPlugin(plugin.taskId, plugin.plugin_unique_identifier) if (runningPluginsLength === 0) setOpen(false) }, [handleClearErrorPlugin, runningPluginsLength]) // Clear handlers using the generic function const handleClearAll = useCallback( () => clearPluginsAndClose([...successPlugins, ...errorPlugins]), [clearPluginsAndClose, successPlugins, errorPlugins], ) const handleClearErrors = useCallback( () => clearPluginsAndClose(errorPlugins), [clearPluginsAndClose, errorPlugins], ) const handleClearSingle = useCallback( (taskId: string, pluginId: string) => clearPluginsAndClose([{ taskId, plugin_unique_identifier: pluginId }]), [clearPluginsAndClose], ) const rootClassName = animatedSlot ? cn( 'flex shrink-0 items-center overflow-visible transition-[width,margin-left,opacity] duration-200 ease-out', hasPluginTasks ? 'ml-1 w-8 opacity-100' : 'ml-0 w-0 opacity-0', ) : 'flex items-center' if (!hasPluginTasks) { if (animatedSlot) return
return null } return (