feat: introduce trigger functionality (#27644)
Signed-off-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: Stream <Stream_2@qq.com> Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com> Co-authored-by: zhsama <torvalds@linux.do> Co-authored-by: Harry <xh001x@hotmail.com> Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: yessenia <yessenia.contact@gmail.com> Co-authored-by: hjlarry <hjlarry@163.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: WTW0313 <twwu@dify.ai> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
} from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import type { PluginDefaultValue } from '@/app/components/workflow/block-selector/types'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import StartNodeSelectionPanel from './start-node-selection-panel'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
|
||||
type WorkflowOnboardingModalProps = {
|
||||
isShow: boolean
|
||||
onClose: () => void
|
||||
onSelectStartNode: (nodeType: BlockEnum, toolConfig?: PluginDefaultValue) => void
|
||||
}
|
||||
|
||||
const WorkflowOnboardingModal: FC<WorkflowOnboardingModalProps> = ({
|
||||
isShow,
|
||||
onClose,
|
||||
onSelectStartNode,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const docLink = useDocLink()
|
||||
|
||||
const handleSelectUserInput = useCallback(() => {
|
||||
onSelectStartNode(BlockEnum.Start)
|
||||
onClose() // Close modal after selection
|
||||
}, [onSelectStartNode, onClose])
|
||||
|
||||
const handleTriggerSelect = useCallback((nodeType: BlockEnum, toolConfig?: PluginDefaultValue) => {
|
||||
onSelectStartNode(nodeType, toolConfig)
|
||||
onClose() // Close modal after selection
|
||||
}, [onSelectStartNode, onClose])
|
||||
|
||||
useEffect(() => {
|
||||
const handleEsc = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && isShow)
|
||||
onClose()
|
||||
}
|
||||
document.addEventListener('keydown', handleEsc)
|
||||
return () => document.removeEventListener('keydown', handleEsc)
|
||||
}, [isShow, onClose])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
isShow={isShow}
|
||||
onClose={onClose}
|
||||
className="w-[618px] max-w-[618px] rounded-2xl border border-effects-highlight bg-background-default-subtle shadow-lg"
|
||||
overlayOpacity
|
||||
closable
|
||||
clickOutsideNotClose
|
||||
>
|
||||
<div className="pb-4">
|
||||
{/* Header */}
|
||||
<div className="mb-6">
|
||||
<h3 className="title-2xl-semi-bold mb-2 text-text-primary">
|
||||
{t('workflow.onboarding.title')}
|
||||
</h3>
|
||||
<div className="body-xs-regular leading-4 text-text-tertiary">
|
||||
{t('workflow.onboarding.description')}{' '}
|
||||
<a
|
||||
href={docLink('/guides/workflow/node/start')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hover:text-text-accent-hover cursor-pointer text-text-accent underline"
|
||||
>
|
||||
{t('workflow.onboarding.learnMore')}
|
||||
</a>{' '}
|
||||
{t('workflow.onboarding.aboutStartNode')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<StartNodeSelectionPanel
|
||||
onSelectUserInput={handleSelectUserInput}
|
||||
onSelectTrigger={handleTriggerSelect}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* ESC tip below modal */}
|
||||
{isShow && (
|
||||
<div className="body-xs-regular pointer-events-none fixed left-1/2 top-1/2 z-[70] flex -translate-x-1/2 translate-y-[165px] items-center gap-1 text-text-quaternary">
|
||||
<span>{t('workflow.onboarding.escTip.press')}</span>
|
||||
<kbd className="system-kbd inline-flex h-4 min-w-4 items-center justify-center rounded bg-components-kbd-bg-gray px-1 text-text-tertiary">
|
||||
{t('workflow.onboarding.escTip.key')}
|
||||
</kbd>
|
||||
<span>{t('workflow.onboarding.escTip.toDismiss')}</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WorkflowOnboardingModal
|
||||
@@ -0,0 +1,53 @@
|
||||
'use client'
|
||||
import type { FC, ReactNode } from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
|
||||
type StartNodeOptionProps = {
|
||||
icon: ReactNode
|
||||
title: string
|
||||
subtitle?: string
|
||||
description: string
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const StartNodeOption: FC<StartNodeOptionProps> = ({
|
||||
icon,
|
||||
title,
|
||||
subtitle,
|
||||
description,
|
||||
onClick,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={cn(
|
||||
'hover:border-components-panel-border-active flex h-40 w-[280px] cursor-pointer flex-col gap-2 rounded-xl border-[0.5px] border-components-option-card-option-border bg-components-panel-on-panel-item-bg p-4 shadow-sm transition-all hover:shadow-md',
|
||||
)}
|
||||
>
|
||||
{/* Icon */}
|
||||
<div className="shrink-0">
|
||||
{icon}
|
||||
</div>
|
||||
|
||||
{/* Text content */}
|
||||
<div className="flex h-[74px] flex-col gap-1 py-0.5">
|
||||
<div className="h-5 leading-5">
|
||||
<h3 className="system-md-semi-bold text-text-primary">
|
||||
{title}
|
||||
{subtitle && (
|
||||
<span className="system-md-regular text-text-quaternary"> {subtitle}</span>
|
||||
)}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="h-12 leading-4">
|
||||
<p className="system-xs-regular text-text-tertiary">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default StartNodeOption
|
||||
@@ -0,0 +1,80 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import StartNodeOption from './start-node-option'
|
||||
import NodeSelector from '@/app/components/workflow/block-selector'
|
||||
import { Home } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import type { PluginDefaultValue } from '@/app/components/workflow/block-selector/types'
|
||||
import { TabsEnum } from '@/app/components/workflow/block-selector/types'
|
||||
|
||||
type StartNodeSelectionPanelProps = {
|
||||
onSelectUserInput: () => void
|
||||
onSelectTrigger: (nodeType: BlockEnum, toolConfig?: PluginDefaultValue) => void
|
||||
}
|
||||
|
||||
const StartNodeSelectionPanel: FC<StartNodeSelectionPanelProps> = ({
|
||||
onSelectUserInput,
|
||||
onSelectTrigger,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [showTriggerSelector, setShowTriggerSelector] = useState(false)
|
||||
|
||||
const handleTriggerClick = useCallback(() => {
|
||||
setShowTriggerSelector(true)
|
||||
}, [])
|
||||
|
||||
const handleTriggerSelect = useCallback((nodeType: BlockEnum, toolConfig?: PluginDefaultValue) => {
|
||||
setShowTriggerSelector(false)
|
||||
onSelectTrigger(nodeType, toolConfig)
|
||||
}, [onSelectTrigger])
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<StartNodeOption
|
||||
icon={
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-[10px] border-[0.5px] border-transparent bg-util-colors-blue-brand-blue-brand-500 p-2">
|
||||
<Home className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
}
|
||||
title={t('workflow.onboarding.userInputFull')}
|
||||
description={t('workflow.onboarding.userInputDescription')}
|
||||
onClick={onSelectUserInput}
|
||||
/>
|
||||
|
||||
<NodeSelector
|
||||
open={showTriggerSelector}
|
||||
onOpenChange={setShowTriggerSelector}
|
||||
onSelect={handleTriggerSelect}
|
||||
placement="right"
|
||||
offset={-200}
|
||||
noBlocks={true}
|
||||
showStartTab={true}
|
||||
defaultActiveTab={TabsEnum.Start}
|
||||
forceShowStartContent={true}
|
||||
availableBlocksTypes={[
|
||||
BlockEnum.TriggerSchedule,
|
||||
BlockEnum.TriggerWebhook,
|
||||
BlockEnum.TriggerPlugin,
|
||||
]}
|
||||
trigger={() => (
|
||||
<StartNodeOption
|
||||
icon={
|
||||
<div className="flex h-9 w-9 items-center justify-center rounded-[10px] border-[0.5px] border-transparent bg-util-colors-blue-brand-blue-brand-500 p-2">
|
||||
<TriggerAll className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
}
|
||||
title={t('workflow.onboarding.trigger')}
|
||||
description={t('workflow.onboarding.triggerDescription')}
|
||||
onClick={handleTriggerClick}
|
||||
/>
|
||||
)}
|
||||
popupClassName="z-[1200]"
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default StartNodeSelectionPanel
|
||||
Reference in New Issue
Block a user