import { memo, useCallback, useEffect, useMemo, } from 'react' import { useNodes } from 'reactflow' import { useTranslation } from 'react-i18next' import BlockIcon from '../block-icon' import type { BlockEnum, CommonNodeType } from '../types' import { BlockEnum as BlockEnumValues } from '../types' // import { useNodeMetaData } from '../hooks' import { START_BLOCKS } from './constants' import type { TriggerDefaultValue } from './types' import Tooltip from '@/app/components/base/tooltip' import { useAvailableNodesMetaData } from '../../workflow-app/hooks' type StartBlocksProps = { searchText: string onSelect: (type: BlockEnum, triggerDefaultValue?: TriggerDefaultValue) => void availableBlocksTypes?: BlockEnum[] onContentStateChange?: (hasContent: boolean) => void hideUserInput?: boolean } const StartBlocks = ({ searchText, onSelect, availableBlocksTypes = [], onContentStateChange, hideUserInput = false, // Allow parent to explicitly hide Start node option (e.g. when one already exists). }: StartBlocksProps) => { const { t } = useTranslation() const nodes = useNodes() // const nodeMetaData = useNodeMetaData() const availableNodesMetaData = useAvailableNodesMetaData() 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 or if hideUserInput is true if (block.type === BlockEnumValues.Start && (hasStartNode || hideUserInput)) return false // Filter by search text 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, t, hideUserInput]) const isEmpty = filteredBlocks.length === 0 useEffect(() => { onContentStateChange?.(!isEmpty) }, [isEmpty, onContentStateChange]) const renderBlock = useCallback((block: { type: BlockEnum; title: string; description?: string }) => (
{block.type === BlockEnumValues.TriggerWebhook ? t('workflow.customWebhook') : t(`workflow.blocks.${block.type}`) }
{t(`workflow.blocksAbout.${block.type}`)}
{(block.type === BlockEnumValues.TriggerWebhook || block.type === BlockEnumValues.TriggerSchedule) && (
{t('tools.author')} {t('workflow.difyTeam')}
)} )} >
onSelect(block.type)} >
{t(`workflow.blocks.${block.type}`)} {block.type === BlockEnumValues.Start && ( {t('workflow.blocks.originalStartNode')} )}
), [availableNodesMetaData, onSelect, t]) if (isEmpty) return null return (
{filteredBlocks.map((block, index) => (
{renderBlock(block)} {block.type === BlockEnumValues.Start && index < filteredBlocks.length - 1 && (
)}
))}
) } export default memo(StartBlocks)