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 (
);
}
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}
>
} />
);
};