From 29deab509c8c196547f43059e023b3017703de52 Mon Sep 17 00:00:00 2001 From: Joel Date: Thu, 3 Jul 2025 16:33:24 +0800 Subject: [PATCH] fix: no data placeholder --- .../assets/vender/line/others/search-menu.svg | 7 ++ .../src/vender/line/others/SearchMenu.json | 77 +++++++++++++++++++ .../src/vender/line/others/SearchMenu.tsx | 20 +++++ .../icons/src/vender/line/others/index.ts | 1 + .../components/tools/add-tool-modal/empty.tsx | 41 ++++++++-- web/app/components/tools/provider-list.tsx | 19 ++++- .../workflow/block-selector/all-tools.tsx | 5 +- .../market-place-plugin/list.tsx | 2 +- .../workflow/block-selector/tools.tsx | 26 ++++--- .../components/agent-strategy-selector.tsx | 11 ++- web/i18n/en-US/tools.ts | 19 ++++- web/i18n/zh-Hans/tools.ts | 19 ++++- 12 files changed, 214 insertions(+), 33 deletions(-) create mode 100644 web/app/components/base/icons/assets/vender/line/others/search-menu.svg create mode 100644 web/app/components/base/icons/src/vender/line/others/SearchMenu.json create mode 100644 web/app/components/base/icons/src/vender/line/others/SearchMenu.tsx diff --git a/web/app/components/base/icons/assets/vender/line/others/search-menu.svg b/web/app/components/base/icons/assets/vender/line/others/search-menu.svg new file mode 100644 index 0000000000..f61f69f4ba --- /dev/null +++ b/web/app/components/base/icons/assets/vender/line/others/search-menu.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/web/app/components/base/icons/src/vender/line/others/SearchMenu.json b/web/app/components/base/icons/src/vender/line/others/SearchMenu.json new file mode 100644 index 0000000000..5222574040 --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/others/SearchMenu.json @@ -0,0 +1,77 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "32", + "height": "32", + "viewBox": "0 0 32 32", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "d": "M28.0049 16C28.0049 20.4183 24.4231 24 20.0049 24C15.5866 24 12.0049 20.4183 12.0049 16C12.0049 11.5817 15.5866 8 20.0049 8C24.4231 8 28.0049 11.5817 28.0049 16Z", + "stroke": "currentColor", + "stroke-width": "2", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M4.00488 16H6.67155", + "stroke": "currentColor", + "stroke-width": "2", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M4.00488 9.33334H8.00488", + "stroke": "currentColor", + "stroke-width": "2", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M4.00488 22.6667H8.00488", + "stroke": "currentColor", + "stroke-width": "2", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M26 22L29.3333 25.3333", + "stroke": "currentColor", + "stroke-width": "2", + "stroke-linecap": "round", + "stroke-linejoin": "round" + }, + "children": [] + } + ] + }, + "name": "SearchMenu" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/line/others/SearchMenu.tsx b/web/app/components/base/icons/src/vender/line/others/SearchMenu.tsx new file mode 100644 index 0000000000..4826abb20f --- /dev/null +++ b/web/app/components/base/icons/src/vender/line/others/SearchMenu.tsx @@ -0,0 +1,20 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './SearchMenu.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconData } from '@/app/components/base/icons/IconBase' + +const Icon = ( + { + ref, + ...props + }: React.SVGProps & { + ref?: React.RefObject>; + }, +) => + +Icon.displayName = 'SearchMenu' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/line/others/index.ts b/web/app/components/base/icons/src/vender/line/others/index.ts index 19d5f1ebb5..2322e9d9f1 100644 --- a/web/app/components/base/icons/src/vender/line/others/index.ts +++ b/web/app/components/base/icons/src/vender/line/others/index.ts @@ -9,4 +9,5 @@ export { default as GlobalVariable } from './GlobalVariable' export { default as Icon3Dots } from './Icon3Dots' export { default as LongArrowLeft } from './LongArrowLeft' export { default as LongArrowRight } from './LongArrowRight' +export { default as SearchMenu } from './SearchMenu' export { default as Tools } from './Tools' diff --git a/web/app/components/tools/add-tool-modal/empty.tsx b/web/app/components/tools/add-tool-modal/empty.tsx index 540d263e99..ef8d7b199c 100644 --- a/web/app/components/tools/add-tool-modal/empty.tsx +++ b/web/app/components/tools/add-tool-modal/empty.tsx @@ -1,19 +1,46 @@ 'use client' -import { useSearchParams } from 'next/navigation' import { useTranslation } from 'react-i18next' -const Empty = () => { +import { ToolTypeEnum } from '../../workflow/block-selector/types' +import { RiArrowRightUpLine } from '@remixicon/react' +import Link from 'next/link' +import cn from '@/utils/classnames' +type Props = { + type?: ToolTypeEnum + isAgent?: boolean +} + +const getLink = (type?: ToolTypeEnum) => { + switch (type) { + case ToolTypeEnum.Custom: + return '/tools?category=api' + case ToolTypeEnum.MCP: + return '/tools?category=mcp' + default: + return '/tools?category=api' + } +} +const Empty = ({ + type, + isAgent, +}: Props) => { const { t } = useTranslation() - const searchParams = useSearchParams() + + const hasLink = type && [ToolTypeEnum.Custom, ToolTypeEnum.MCP].includes(type) + const Comp = (hasLink ? Link : 'div') as any + const linkProps = hasLink ? { href: getLink(type), target: '_blank' } : {} + const renderType = isAgent ? 'agent' : type return (
- {t(`tools.addToolModal.${searchParams.get('category') === 'workflow' ? 'emptyTitle' : 'emptyTitleCustom'}`)} -
-
- {t(`tools.addToolModal.${searchParams.get('category') === 'workflow' ? 'emptyTip' : 'emptyTipCustom'}`)} + {t(`tools.addToolModal.${renderType}.title`)}
+ {!isAgent && ( + + {t(`tools.addToolModal.${renderType}.tip`)} {hasLink && } + + )}
) } diff --git a/web/app/components/tools/provider-list.tsx b/web/app/components/tools/provider-list.tsx index 5a3e2e7f94..ecfa5f6ea2 100644 --- a/web/app/components/tools/provider-list.tsx +++ b/web/app/components/tools/provider-list.tsx @@ -19,8 +19,25 @@ import MCPList from './mcp' import { useAllToolProviders } from '@/service/use-tools' import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' import { useGlobalPublicStore } from '@/context/global-public-context' +import { ToolTypeEnum } from '../workflow/block-selector/types' +const getToolType = (type: string) => { + switch (type) { + case 'builtin': + return ToolTypeEnum.BuiltIn + case 'api': + return ToolTypeEnum.Custom + case 'workflow': + return ToolTypeEnum.Workflow + case 'mcp': + return ToolTypeEnum.MCP + default: + return ToolTypeEnum.BuiltIn + } +} const ProviderList = () => { + // const searchParams = useSearchParams() + // searchParams.get('category') === 'workflow' const { t } = useTranslation() const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) const containerRef = useRef(null) @@ -131,7 +148,7 @@ const ProviderList = () => { /> ))} - {!filteredCollectionList.length && activeTab === 'workflow' &&
} + {!filteredCollectionList.length && activeTab === 'workflow' &&
} )} {!filteredCollectionList.length && activeTab === 'builtin' && ( diff --git a/web/app/components/workflow/block-selector/all-tools.tsx b/web/app/components/workflow/block-selector/all-tools.tsx index 16bdefc814..a069d312c4 100644 --- a/web/app/components/workflow/block-selector/all-tools.tsx +++ b/web/app/components/workflow/block-selector/all-tools.tsx @@ -93,9 +93,8 @@ const AllTools = ({ } = useMarketplacePlugins() const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) - useEffect(() => { - if (enable_marketplace) return + if (!enable_marketplace) return if (searchText || tags.length > 0) { fetchPlugins({ query: searchText, @@ -142,11 +141,11 @@ const AllTools = ({ > (({ ) } - const maxWidthClassName = toolContentClassName || 'max-w-[300px]' + const maxWidthClassName = toolContentClassName || 'max-w-[100%]' return ( <> diff --git a/web/app/components/workflow/block-selector/tools.tsx b/web/app/components/workflow/block-selector/tools.tsx index 14568fb1d7..1721dd72ff 100644 --- a/web/app/components/workflow/block-selector/tools.tsx +++ b/web/app/components/workflow/block-selector/tools.tsx @@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next' import type { BlockEnum, ToolWithProvider } from '../types' import IndexBar, { groupItems } from './index-bar' import type { ToolDefaultValue, ToolValue } from './types' +import type { ToolTypeEnum } from './types' import { ViewType } from './view-type-select' import Empty from '@/app/components/tools/add-tool-modal/empty' import { useGetLanguage } from '@/context/i18n' @@ -15,33 +16,36 @@ import ToolListFlatView from './tool/tool-list-flat-view/list' import classNames from '@/utils/classnames' type ToolsProps = { - showWorkflowEmpty: boolean onSelect: (type: BlockEnum, tool?: ToolDefaultValue) => void canNotSelectMultiple?: boolean onSelectMultiple?: (type: BlockEnum, tools: ToolDefaultValue[]) => void tools: ToolWithProvider[] viewType: ViewType hasSearchText: boolean + toolType?: ToolTypeEnum + isAgent?: boolean className?: string indexBarClassName?: string selectedTools?: ToolValue[] canChooseMCPTool?: boolean - hasScrollBar: boolean + hasScrollBar?: boolean } const Blocks = ({ - showWorkflowEmpty, onSelect, canNotSelectMultiple, onSelectMultiple, tools, viewType, hasSearchText, + toolType, + isAgent, className, indexBarClassName, selectedTools, canChooseMCPTool, hasScrollBar, }: ToolsProps) => { + // const tools: any = [] const { t } = useTranslation() const language = useGetLanguage() const isFlatView = viewType === ViewType.flat @@ -95,15 +99,19 @@ const Blocks = ({ const toolRefs = useRef({}) return ( -
+
{ - !tools.length && !showWorkflowEmpty && ( -
{t('workflow.tabs.noResult')}
+ !tools.length && hasSearchText && ( + //
+ // + // Small spacing for marketplace search results +
{t('workflow.tabs.noResult')}
+ //
) } - {!tools.length && showWorkflowEmpty && ( + {!tools.length && !hasSearchText && (
- +
)} {!!tools.length && ( @@ -133,7 +141,7 @@ const Blocks = ({ ) )} - {isShowLetterIndex && } + {isShowLetterIndex && }
) } diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx index 61ad2e3b92..4c1fcb2967 100644 --- a/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx +++ b/web/app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx @@ -22,6 +22,7 @@ import type { ListRef } from '@/app/components/workflow/block-selector/market-pl import PluginList, { type ListProps } from '@/app/components/workflow/block-selector/market-place-plugin/list' import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hooks' import { ToolTipContent } from '@/app/components/base/tooltip/content' +import { useGlobalPublicStore } from '@/context/global-public-context' const DEFAULT_TAGS: ListProps['tags'] = [] @@ -93,6 +94,8 @@ export type AgentStrategySelectorProps = { } export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => { + const { enable_marketplace } = useGlobalPublicStore(s => s.systemFeatures) + const { value, onChange, canChooseMCPTool } = props const [open, setOpen] = useState(false) const [viewType, setViewType] = useState(ViewType.flat) @@ -160,7 +163,7 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => alt='icon' />
}

{value?.agent_strategy_label || t('workflow.nodes.agent.strategy.selectTip')}

@@ -218,19 +221,19 @@ export const AgentStrategySelector = memo((props: AgentStrategySelectorProps) => }} className='h-full max-h-full max-w-none overflow-y-auto' indexBarClassName='top-0 xl:top-36' - showWorkflowEmpty={false} hasSearchText={false} canNotSelectMultiple canChooseMCPTool={canChooseMCPTool} + isAgent /> - + />} diff --git a/web/i18n/en-US/tools.ts b/web/i18n/en-US/tools.ts index 1988e14bda..1765ecf241 100644 --- a/web/i18n/en-US/tools.ts +++ b/web/i18n/en-US/tools.ts @@ -28,10 +28,21 @@ const translation = { add: 'add', added: 'added', manageInTools: 'Manage in Tools', - emptyTitle: 'No workflow tool available', - emptyTip: 'Go to "Workflow -> Publish as Tool"', - emptyTitleCustom: 'No custom tool available', - emptyTipCustom: 'Create a custom tool', + custom: { + title: 'No custom tool available', + tip: 'Create a custom tool', + }, + workflow: { + title: 'No workflow tool available', + tip: 'Publish workflows as tools in Studio', + }, + mcp: { + title: 'No MCP tool available', + tip: 'Add a MCP server', + }, + agent: { + title: 'No agent strategy available', + }, }, createTool: { title: 'Create Custom Tool', diff --git a/web/i18n/zh-Hans/tools.ts b/web/i18n/zh-Hans/tools.ts index fa2ee2ac1e..4e0ccf476f 100644 --- a/web/i18n/zh-Hans/tools.ts +++ b/web/i18n/zh-Hans/tools.ts @@ -28,10 +28,21 @@ const translation = { add: '添加', added: '已添加', manageInTools: '去工具列表管理', - emptyTitle: '没有可用的工作流工具', - emptyTip: '去“工作流 -> 发布为工具”添加', - emptyTitleCustom: '没有可用的自定义工具', - emptyTipCustom: '创建自定义工具', + custom: { + title: '没有可用的自定义工具', + tip: '创建自定义工具', + }, + workflow: { + title: '没有可用的工作流工具', + tip: '在工作室中发布工作流作为工具', + }, + mcp: { + title: '没有可用的 MCP 工具', + tip: '添加 MCP 服务器', + }, + agent: { + title: '没有可用的 agent 策略', + }, }, createTool: { title: '创建自定义工具',