2022-10-30 11:54:14 +08:00
|
|
|
import React, { useEffect } from 'react';
|
|
|
|
import { Link, useHistory } from 'react-router-dom';
|
2022-06-02 00:18:55 +08:00
|
|
|
import { Dropdown, Menu, Button, Tag, Switch, message } from 'antd';
|
2022-10-30 11:54:14 +08:00
|
|
|
import { DownOutlined, RightOutlined } from '@ant-design/icons';
|
2022-03-27 15:51:48 +08:00
|
|
|
import { cx } from '@emotion/css';
|
2022-05-02 10:10:22 +08:00
|
|
|
import { useTranslation } from 'react-i18next';
|
2022-10-30 11:54:14 +08:00
|
|
|
import classnames from 'classnames';
|
2022-03-27 15:51:48 +08:00
|
|
|
|
|
|
|
import {
|
2022-05-05 20:55:35 +08:00
|
|
|
useDocumentTitle,
|
2022-05-12 12:19:25 +08:00
|
|
|
useResourceActionContext,
|
|
|
|
useResourceContext
|
2022-06-29 23:42:03 +08:00
|
|
|
} from '@nocobase/client';
|
|
|
|
|
2022-10-30 11:54:14 +08:00
|
|
|
import { FlowContext } from './FlowContext';
|
|
|
|
import { branchBlockClass, nodeCardClass, nodeMetaClass, workflowVersionDropdownClass } from './style';
|
2022-05-12 12:19:25 +08:00
|
|
|
import { TriggerConfig } from './triggers';
|
2022-10-30 11:54:14 +08:00
|
|
|
import { Branch } from './Branch';
|
2022-03-27 15:51:48 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function WorkflowCanvas() {
|
2022-04-30 10:06:25 +08:00
|
|
|
const { t } = useTranslation();
|
2022-05-12 12:19:25 +08:00
|
|
|
const history = useHistory();
|
2022-03-27 15:51:48 +08:00
|
|
|
const { data, refresh, loading } = useResourceActionContext();
|
2022-05-12 12:19:25 +08:00
|
|
|
const { resource, targetKey } = useResourceContext();
|
2022-05-05 20:55:35 +08:00
|
|
|
const { setTitle } = useDocumentTitle();
|
|
|
|
useEffect(() => {
|
|
|
|
const { title } = data?.data ?? {};
|
2022-10-30 11:54:14 +08:00
|
|
|
setTitle(`${t('Workflow')}${title ? `: ${title}` : ''}`);
|
2022-05-05 20:55:35 +08:00
|
|
|
}, [data?.data]);
|
|
|
|
|
2022-03-27 15:51:48 +08:00
|
|
|
if (!data?.data && !loading) {
|
2022-04-30 10:06:25 +08:00
|
|
|
return <div>{t('Load failed')}</div>;
|
2022-03-27 15:51:48 +08:00
|
|
|
}
|
|
|
|
|
2022-05-12 12:19:25 +08:00
|
|
|
const { nodes = [], revisions = [], ...workflow } = data?.data ?? {};
|
2022-03-27 15:51:48 +08:00
|
|
|
|
|
|
|
makeNodes(nodes);
|
|
|
|
|
|
|
|
const entry = nodes.find(item => !item.upstream);
|
|
|
|
|
2022-05-12 12:19:25 +08:00
|
|
|
function onSwitchVersion({ key }) {
|
|
|
|
if (key != workflow.id) {
|
|
|
|
history.push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async function onToggle(value) {
|
|
|
|
await resource.update({
|
|
|
|
filterByTk: workflow[targetKey],
|
|
|
|
values: {
|
|
|
|
enabled: value,
|
2022-10-30 11:54:14 +08:00
|
|
|
// NOTE: keep `key` field to adapt for backend
|
2022-05-12 12:19:25 +08:00
|
|
|
key: workflow.key
|
|
|
|
}
|
|
|
|
});
|
|
|
|
refresh();
|
|
|
|
}
|
|
|
|
|
2022-06-09 16:40:10 +08:00
|
|
|
async function onRevision() {
|
|
|
|
const { data: { data: revision } } = await resource.revision({
|
2022-05-12 12:19:25 +08:00
|
|
|
filterByTk: workflow[targetKey]
|
|
|
|
});
|
2022-06-02 00:18:55 +08:00
|
|
|
message.success(t('Operation succeeded'));
|
2022-05-12 12:19:25 +08:00
|
|
|
|
2022-06-09 16:40:10 +08:00
|
|
|
history.push(`${revision.id}`);
|
2022-05-12 12:19:25 +08:00
|
|
|
}
|
|
|
|
|
2022-03-27 15:51:48 +08:00
|
|
|
return (
|
|
|
|
<FlowContext.Provider value={{
|
2022-04-11 22:06:41 +08:00
|
|
|
workflow,
|
2022-03-27 15:51:48 +08:00
|
|
|
nodes,
|
|
|
|
onNodeAdded: refresh,
|
|
|
|
onNodeRemoved: refresh
|
|
|
|
}}>
|
2022-05-12 12:19:25 +08:00
|
|
|
<div className="workflow-toolbar">
|
|
|
|
<header>
|
2022-10-30 11:54:14 +08:00
|
|
|
<span>
|
|
|
|
<Link to={`/admin/settings/workflow/workflows`}>
|
|
|
|
{t('Workflow')}
|
|
|
|
</Link>
|
|
|
|
</span>
|
2022-05-12 12:19:25 +08:00
|
|
|
<strong>{workflow.title}</strong>
|
|
|
|
</header>
|
|
|
|
<aside>
|
|
|
|
<div className="workflow-versions">
|
|
|
|
<label>{t('Version')}</label>
|
|
|
|
<Dropdown
|
|
|
|
trigger={['click']}
|
|
|
|
overlay={
|
|
|
|
<Menu
|
|
|
|
onClick={onSwitchVersion}
|
|
|
|
defaultSelectedKeys={[workflow.id]}
|
|
|
|
className={cx(workflowVersionDropdownClass)}
|
|
|
|
>
|
2022-10-30 11:54:14 +08:00
|
|
|
{revisions.sort((a, b) => b.id - a.id).map((item, index) => (
|
2022-05-12 12:19:25 +08:00
|
|
|
<Menu.Item
|
|
|
|
key={item.id}
|
|
|
|
icon={item.current ? <RightOutlined /> : null}
|
2022-10-30 11:54:14 +08:00
|
|
|
className={classnames({
|
|
|
|
executed: item.executed,
|
|
|
|
unexecuted: !item.executed,
|
|
|
|
enabled: item.enabled,
|
|
|
|
})}
|
2022-05-12 12:19:25 +08:00
|
|
|
>
|
|
|
|
<strong>{`#${item.id}`}</strong>
|
|
|
|
<time>{(new Date(item.createdAt)).toLocaleString()}</time>
|
|
|
|
</Menu.Item>
|
|
|
|
))}
|
|
|
|
</Menu>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<Button type="link">{workflow?.id ? `#${workflow.id}` : null}<DownOutlined /></Button>
|
|
|
|
</Dropdown>
|
|
|
|
</div>
|
|
|
|
<Switch
|
|
|
|
checked={workflow.enabled}
|
|
|
|
onChange={onToggle}
|
2022-06-02 00:18:55 +08:00
|
|
|
checkedChildren={t('On')}
|
|
|
|
unCheckedChildren={t('Off')}
|
2022-05-12 12:19:25 +08:00
|
|
|
/>
|
|
|
|
{workflow.executed && !revisions.find(item => !item.executed && new Date(item.createdAt) > new Date(workflow.createdAt))
|
|
|
|
? (
|
2022-06-09 16:40:10 +08:00
|
|
|
<Button onClick={onRevision}>{t('Copy to new version')}</Button>
|
2022-05-12 12:19:25 +08:00
|
|
|
)
|
|
|
|
: null
|
|
|
|
}
|
|
|
|
</aside>
|
|
|
|
</div>
|
|
|
|
<div className="workflow-canvas">
|
2022-10-30 11:54:14 +08:00
|
|
|
<TriggerConfig workflow={workflow} />
|
2022-05-12 12:19:25 +08:00
|
|
|
<div className={branchBlockClass}>
|
|
|
|
<Branch entry={entry} />
|
|
|
|
</div>
|
|
|
|
<div className={cx(nodeCardClass)}>
|
|
|
|
<div className={cx(nodeMetaClass)}>
|
|
|
|
<Tag color="#333">{t('End')}</Tag>
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-03-27 15:51:48 +08:00
|
|
|
</div>
|
|
|
|
</FlowContext.Provider>
|
|
|
|
);
|
|
|
|
}
|