mirror of https://github.com/langgenius/dify.git
fix: localize workflow block search filters
This commit is contained in:
parent
f9df61e648
commit
f06dc3ef90
|
|
@ -34,6 +34,7 @@ import Link from 'next/link'
|
|||
import Divider from '@/app/components/base/divider'
|
||||
import { RiArrowRightUpLine } from '@remixicon/react'
|
||||
import { getMarketplaceUrl } from '@/utils/var'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
|
||||
const marketplaceFooterClassName = 'system-sm-medium z-10 flex h-8 flex-none cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg'
|
||||
|
||||
|
|
@ -83,6 +84,7 @@ const AllTools = ({
|
|||
onFeaturedInstallSuccess,
|
||||
}: AllToolsProps) => {
|
||||
const { t } = useTranslation()
|
||||
const language = useGetLanguage()
|
||||
const tabs = useToolTabs()
|
||||
const [activeTab, setActiveTab] = useState(ToolTypeEnum.All)
|
||||
const [activeView, setActiveView] = useState<ViewType>(ViewType.flat)
|
||||
|
|
@ -117,12 +119,29 @@ const AllTools = ({
|
|||
mergedTools = mcpTools
|
||||
|
||||
const normalizedSearch = trimmedSearchText.toLowerCase()
|
||||
const getLocalizedText = (text?: Record<string, string> | null) => {
|
||||
if (!text)
|
||||
return ''
|
||||
|
||||
if (text[language])
|
||||
return text[language]
|
||||
|
||||
if (text['en-US'])
|
||||
return text['en-US']
|
||||
|
||||
const firstValue = Object.values(text).find(Boolean)
|
||||
return firstValue || ''
|
||||
}
|
||||
|
||||
if (!hasFilter || !normalizedSearch)
|
||||
return mergedTools.filter(toolWithProvider => toolWithProvider.tools.length > 0)
|
||||
|
||||
return mergedTools.reduce<ToolWithProvider[]>((acc, toolWithProvider) => {
|
||||
const providerMatches = isMatchingKeywords(toolWithProvider.name, normalizedSearch)
|
||||
const providerLabel = getLocalizedText(toolWithProvider.label)
|
||||
const providerMatches = [
|
||||
toolWithProvider.name,
|
||||
providerLabel,
|
||||
].some(text => isMatchingKeywords(text || '', normalizedSearch))
|
||||
|
||||
if (providerMatches) {
|
||||
if (toolWithProvider.tools.length > 0)
|
||||
|
|
@ -131,7 +150,11 @@ const AllTools = ({
|
|||
}
|
||||
|
||||
const matchedTools = toolWithProvider.tools.filter((tool) => {
|
||||
return tool.name.toLowerCase().includes(normalizedSearch)
|
||||
const toolLabel = getLocalizedText(tool.label)
|
||||
return [
|
||||
tool.name,
|
||||
toolLabel,
|
||||
].some(text => isMatchingKeywords(text || '', normalizedSearch))
|
||||
})
|
||||
|
||||
if (matchedTools.length > 0) {
|
||||
|
|
@ -143,7 +166,7 @@ const AllTools = ({
|
|||
|
||||
return acc
|
||||
}, [])
|
||||
}, [activeTab, buildInTools, customTools, workflowTools, mcpTools, trimmedSearchText, hasFilter])
|
||||
}, [activeTab, buildInTools, customTools, workflowTools, mcpTools, trimmedSearchText, hasFilter, language])
|
||||
|
||||
const {
|
||||
queryPluginsWithDebounced: fetchPlugins,
|
||||
|
|
@ -235,39 +258,37 @@ const AllTools = ({
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
{(hasToolsListContent || enable_marketplace) && (
|
||||
{hasToolsListContent && (
|
||||
<>
|
||||
<div className='px-3 pb-1 pt-2'>
|
||||
<span className='system-xs-medium text-text-primary'>{t('tools.allTools')}</span>
|
||||
</div>
|
||||
{hasToolsListContent && (
|
||||
<Tools
|
||||
className={toolContentClassName}
|
||||
tools={tools}
|
||||
onSelect={onSelect}
|
||||
canNotSelectMultiple={canNotSelectMultiple}
|
||||
onSelectMultiple={onSelectMultiple}
|
||||
toolType={activeTab}
|
||||
viewType={isSupportGroupView ? activeView : ViewType.flat}
|
||||
hasSearchText={hasSearchText}
|
||||
selectedTools={selectedTools}
|
||||
canChooseMCPTool={canChooseMCPTool}
|
||||
isShowRAGRecommendations={isShowRAGRecommendations}
|
||||
/>
|
||||
)}
|
||||
{enable_marketplace && (
|
||||
<PluginList
|
||||
ref={pluginRef}
|
||||
wrapElemRef={wrapElemRef as RefObject<HTMLElement>}
|
||||
list={notInstalledPlugins}
|
||||
searchText={searchText}
|
||||
toolContentClassName={toolContentClassName}
|
||||
tags={tags}
|
||||
hideFindMoreFooter
|
||||
/>
|
||||
)}
|
||||
<Tools
|
||||
className={toolContentClassName}
|
||||
tools={tools}
|
||||
onSelect={onSelect}
|
||||
canNotSelectMultiple={canNotSelectMultiple}
|
||||
onSelectMultiple={onSelectMultiple}
|
||||
toolType={activeTab}
|
||||
viewType={isSupportGroupView ? activeView : ViewType.flat}
|
||||
hasSearchText={hasSearchText}
|
||||
selectedTools={selectedTools}
|
||||
canChooseMCPTool={canChooseMCPTool}
|
||||
isShowRAGRecommendations={isShowRAGRecommendations}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{enable_marketplace && (
|
||||
<PluginList
|
||||
ref={pluginRef}
|
||||
wrapElemRef={wrapElemRef as RefObject<HTMLElement>}
|
||||
list={notInstalledPlugins}
|
||||
searchText={searchText}
|
||||
toolContentClassName={toolContentClassName}
|
||||
tags={tags}
|
||||
hideFindMoreFooter
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{shouldShowEmptyState && (
|
||||
|
|
|
|||
|
|
@ -36,6 +36,13 @@ const StartBlocks = ({
|
|||
const filteredBlocks = useMemo(() => {
|
||||
// Check if Start node already exists in workflow
|
||||
const hasStartNode = nodes.some(node => (node.data as CommonNodeType)?.type === BlockEnumValues.Start)
|
||||
const normalizedSearch = searchText.toLowerCase()
|
||||
const getDisplayName = (blockType: BlockEnum) => {
|
||||
if (blockType === BlockEnumValues.TriggerWebhook)
|
||||
return t('workflow.customWebhook')
|
||||
|
||||
return t(`workflow.blocks.${blockType}`)
|
||||
}
|
||||
|
||||
return START_BLOCKS.filter((block) => {
|
||||
// Hide User Input (Start) if it already exists in workflow
|
||||
|
|
@ -43,13 +50,14 @@ const StartBlocks = ({
|
|||
return false
|
||||
|
||||
// Filter by search text
|
||||
if (!block.title.toLowerCase().includes(searchText.toLowerCase()))
|
||||
const displayName = getDisplayName(block.type).toLowerCase()
|
||||
if (!displayName.includes(normalizedSearch) && !block.title.toLowerCase().includes(normalizedSearch))
|
||||
return false
|
||||
|
||||
// availableBlocksTypes now contains properly filtered entry node types from parent
|
||||
return availableBlocksTypes.includes(block.type)
|
||||
})
|
||||
}, [searchText, availableBlocksTypes, nodes])
|
||||
}, [searchText, availableBlocksTypes, nodes, t])
|
||||
|
||||
const isEmpty = filteredBlocks.length === 0
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useAllTriggerPlugins } from '@/service/use-triggers'
|
|||
import TriggerPluginItem from './item'
|
||||
import type { BlockEnum } from '../../types'
|
||||
import type { TriggerDefaultValue, TriggerWithProvider } from '../types'
|
||||
import { useGetLanguage } from '@/context/i18n'
|
||||
|
||||
type TriggerPluginListProps = {
|
||||
onSelect: (type: BlockEnum, trigger?: TriggerDefaultValue) => void
|
||||
|
|
@ -18,10 +19,30 @@ const TriggerPluginList = ({
|
|||
onContentStateChange,
|
||||
}: TriggerPluginListProps) => {
|
||||
const { data: triggerPluginsData } = useAllTriggerPlugins()
|
||||
const language = useGetLanguage()
|
||||
|
||||
const normalizedSearch = searchText.trim().toLowerCase()
|
||||
const triggerPlugins = useMemo(() => {
|
||||
const plugins = triggerPluginsData || []
|
||||
const getLocalizedText = (text?: Record<string, string> | null) => {
|
||||
if (!text)
|
||||
return ''
|
||||
|
||||
if (text[language])
|
||||
return text[language]
|
||||
|
||||
if (text['en-US'])
|
||||
return text['en-US']
|
||||
|
||||
const firstValue = Object.values(text).find(Boolean)
|
||||
return (typeof firstValue === 'string') ? firstValue : ''
|
||||
}
|
||||
const getSearchableTexts = (name: string, label?: Record<string, string> | null) => {
|
||||
const localized = getLocalizedText(label)
|
||||
const values = [localized, name].filter(Boolean)
|
||||
return values.length > 0 ? values : ['']
|
||||
}
|
||||
const isMatchingKeywords = (value: string) => value.toLowerCase().includes(normalizedSearch)
|
||||
|
||||
if (!normalizedSearch)
|
||||
return plugins.filter(triggerWithProvider => triggerWithProvider.events.length > 0)
|
||||
|
|
@ -30,7 +51,10 @@ const TriggerPluginList = ({
|
|||
if (triggerWithProvider.events.length === 0)
|
||||
return acc
|
||||
|
||||
const providerMatches = triggerWithProvider.name.toLowerCase().includes(normalizedSearch)
|
||||
const providerMatches = getSearchableTexts(
|
||||
triggerWithProvider.name,
|
||||
triggerWithProvider.label,
|
||||
).some(text => isMatchingKeywords(text))
|
||||
|
||||
if (providerMatches) {
|
||||
acc.push(triggerWithProvider)
|
||||
|
|
@ -38,7 +62,10 @@ const TriggerPluginList = ({
|
|||
}
|
||||
|
||||
const matchedEvents = triggerWithProvider.events.filter((event) => {
|
||||
return event.name.toLowerCase().includes(normalizedSearch)
|
||||
return getSearchableTexts(
|
||||
event.name,
|
||||
event.label,
|
||||
).some(text => isMatchingKeywords(text))
|
||||
})
|
||||
|
||||
if (matchedEvents.length > 0) {
|
||||
|
|
@ -50,7 +77,7 @@ const TriggerPluginList = ({
|
|||
|
||||
return acc
|
||||
}, [])
|
||||
}, [triggerPluginsData, normalizedSearch])
|
||||
}, [triggerPluginsData, normalizedSearch, language])
|
||||
|
||||
const hasContent = triggerPlugins.length > 0
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue