import React, { useContext, useEffect } from 'react'; import { useHistory } from 'react-router-dom'; import { Dropdown, Menu, Button, Tag, Switch, message } from 'antd'; import { PlusOutlined, DownOutlined, RightOutlined } from '@ant-design/icons'; import { cx } from '@emotion/css'; import { useTranslation } from 'react-i18next'; import { useAPIClient, useCompile, useDocumentTitle, useResourceActionContext, useResourceContext } from '@nocobase/client'; import { Instruction, instructions, Node } from './nodes'; import { addButtonClass, branchBlockClass, branchClass, nodeCardClass, nodeMetaClass, workflowVersionDropdownClass } from './style'; import { TriggerConfig } from './triggers'; function makeNodes(nodes): void { const nodesMap = new Map(); nodes.forEach(item => nodesMap.set(item.id, item)); for (let node of nodesMap.values()) { if (node.upstreamId) { node.upstream = nodesMap.get(node.upstreamId); } if (node.downstreamId) { node.downstream = nodesMap.get(node.downstreamId); } } } const FlowContext = React.createContext(null); export function useFlowContext() { return useContext(FlowContext); } export function WorkflowCanvas() { const { t } = useTranslation(); const history = useHistory(); const { data, refresh, loading } = useResourceActionContext(); const { resource, targetKey } = useResourceContext(); const { setTitle } = useDocumentTitle(); useEffect(() => { const { title } = data?.data ?? {}; setTitle(`${title ? `${title} - ` : ''}${t('Workflow')}`); }, [data?.data]); if (!data?.data && !loading) { return
{t('Load failed')}
; } const { nodes = [], revisions = [], ...workflow } = data?.data ?? {}; makeNodes(nodes); const entry = nodes.find(item => !item.upstream); function onSwitchVersion({ key }) { if (key != workflow.id) { history.push(key); } } async function onToggle(value) { await resource.update({ filterByTk: workflow[targetKey], values: { enabled: value, // NOTE: keep `key` field to adapter for backend key: workflow.key } }); refresh(); } async function onRevision() { const { data: { data: revision } } = await resource.revision({ filterByTk: workflow[targetKey] }); message.success(t('Operation succeeded')); history.push(`${revision.id}`); } return (
{workflow.title}
{t('End')}
); } export function Branch({ from = null, entry = null, branchIndex = null, controller = null }) { const list = []; for (let node = entry; node; node = node.downstream) { list.push(node); } return (
{controller}
{list.map(item => )}
); } // TODO(bug): useless observable // const instructionsList = observable(Array.from(instructions.getValues())); interface AddButtonProps { upstream; branchIndex?: number; }; export function AddButton({ upstream, branchIndex = null }: AddButtonProps) { const compile = useCompile(); const api = useAPIClient(); const { workflow, onNodeAdded } = useFlowContext(); const resource = api.resource('workflows.nodes', workflow.id); async function onCreate({ keyPath }) { const type = keyPath.pop(); const config = {}; const [optionKey] = keyPath; if (optionKey) { const { value } = instructions.get(type).options.find(item => item.key === optionKey); Object.assign(config, value); } const { data: { data: node } } = await resource.create({ values: { type, upstreamId: upstream?.id ?? null, branchIndex, config } }); onNodeAdded(node); } const groups = [ { value: 'control', name: '{{t("Control")}}' }, { value: 'collection', name: '{{t("Collection operations")}}' }, ]; const instructionList = (Array.from(instructions.getValues()) as Instruction[]); return (
onCreate(ev)}> {groups.map(group => ( {instructionList.filter(item => item.group === group.value).map(item => item.options ? ( {item.options.map(option => ( {compile(option.label)} ))} ) : ( {compile(item.title)} ))} ))} } disabled={workflow.executed} >
); };