@gadmin2n/schematics 0.0.72 → 0.0.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/dist/index.d.ts +2 -0
  2. package/dist/index.js +2 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/job.prisma +62 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/system.prisma +0 -21
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +171 -0
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/AgendaJob.ts +60 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +1 -1
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowEventOutbox.ts +62 -0
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowNodeInstance.ts +62 -0
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowNodeType.ts +62 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.env +5 -0
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +5 -4
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/prisma.config.ts +14 -7
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +4 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permissions.ts +49 -3
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +746 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflows.ts +786 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +6 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.service.ts +79 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.spec.ts +20 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.controller.ts +145 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.module.ts +10 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/{canvas/canvas.service.spec.ts → agendaJob/agendaJob.service.spec.ts} +71 -65
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agendaJob/agendaJob.service.ts +83 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +2 -1
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.module.ts +9 -0
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +100 -0
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution.dto.ts +19 -0
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.dto.ts +43 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-export.service.ts +317 -0
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-node-type.controller.ts +16 -0
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-node-type.service.ts +13 -0
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +220 -0
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.dto.ts +82 -0
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.module.ts +16 -0
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +505 -0
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.spec.ts +22 -0
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.controller.ts +147 -0
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.module.ts +10 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.spec.ts +356 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowEventOutbox/workflowEventOutbox.service.ts +110 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.spec.ts +22 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.ts +216 -0
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.module.ts +10 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.spec.ts +356 -0
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.service.ts +168 -0
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.spec.ts +22 -0
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.ts +199 -0
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.module.ts +10 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.spec.ts +348 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.service.ts +106 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/yarn.lock +579 -1082
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/README.md +278 -0
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/config/development-sql.yaml +5 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/docker-compose.yml +25 -0
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/package.json +13 -0
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/sql/create-event-trigger.sql +87 -0
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/.env +7 -0
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/SANDBOX.md +122 -0
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/package-lock.json +4285 -0
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/package.json +28 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/activities/code-execute.test.ts +44 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/activities/http-request.test.ts +87 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/helpers.test.ts +225 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/__tests__/node-type-consistency.test.ts +101 -0
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/code-execute.ts +51 -0
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/db-execute.ts +85 -0
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/db-query.ts +35 -0
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/http-request.ts +54 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/index.ts +6 -0
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/reporting.ts +62 -0
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/activities/send-notification.ts +47 -0
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/config.ts +13 -0
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/condition.ts +101 -0
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/context.ts +58 -0
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/graph.ts +184 -0
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/helpers.ts +133 -0
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/node-types.ts +57 -0
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/dsl/types.ts +77 -0
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/index.ts +36 -0
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/outbox-poller.ts +226 -0
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/src/workflows/dsl-workflow.ts +411 -0
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/tsconfig.json +19 -0
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/vitest.config.ts +8 -0
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/temporal/worker/yarn.lock +1905 -0
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package-lock.json +17555 -0
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +5 -2
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/App.tsx +1 -0
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/sider.tsx +5 -1
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/layout/title.tsx +1 -1
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +63 -0
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/DevShell.tsx +91 -2
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/list.tsx +48 -2
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/show.tsx +43 -2
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +14 -9
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +14 -9
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +309 -56
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/show.tsx +1 -3
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/create.tsx +108 -0
  100. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/edit.tsx +124 -0
  101. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/index.tsx +4 -0
  102. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +245 -0
  103. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/show.tsx +70 -0
  104. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +0 -1
  105. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +160 -2
  106. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +120 -148
  107. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CodeFloatWindow.tsx +74 -181
  108. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/LivePreview.tsx +15 -13
  109. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasConfigRegistry.tsx +2 -2
  110. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasContextMenuRegistry.tsx +338 -3
  111. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/canvasDefaults.ts +18 -17
  112. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/BarChartDataSourceModal.tsx +10 -4
  113. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/LineChartDataSourceModal.tsx +10 -4
  114. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/{ChartViewerConfigModal.tsx → MultiChartConfigModal.tsx} +30 -18
  115. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/MultiChartDataSourceModal.tsx +427 -0
  116. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/NumCardDataSourceModal.tsx +10 -4
  117. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/PromptModal.tsx +6 -14
  118. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/RadarChartDataSourceModal.tsx +10 -4
  119. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/TableDataSourceModal.tsx +10 -4
  120. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/canvasModalProps.ts +24 -0
  121. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/demos.ts +45 -63
  122. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CustomNode.tsx +99 -0
  123. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ExportModal.tsx +87 -0
  124. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/FlowRenderer.tsx +322 -0
  125. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ImportModal.tsx +175 -0
  126. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeEditModal.tsx +60 -0
  127. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +1150 -0
  128. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/RunWorkflowModal.tsx +101 -0
  129. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/StatusCards.tsx +198 -0
  130. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/VersionPanel.tsx +81 -0
  131. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +566 -0
  132. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/hooks/useWorkflowAgent.ts +224 -0
  133. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +524 -0
  134. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +343 -0
  135. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx +243 -0
  136. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/CreateNodeInstanceModal.tsx +363 -0
  137. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/DynamicConfigForm.tsx +154 -0
  138. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/NodeInstanceForm.tsx +176 -0
  139. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/create.tsx +77 -0
  140. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/edit.tsx +112 -0
  141. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/index.tsx +305 -0
  142. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +282 -0
  143. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +469 -0
  144. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +92 -0
  145. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/create.tsx +111 -0
  146. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/edit.tsx +127 -0
  147. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/index.tsx +4 -0
  148. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/list.tsx +254 -0
  149. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/show.tsx +74 -0
  150. package/dist/lib/application/files/gadmin2-game-angle-demo/web/yarn.lock +1501 -1199
  151. package/package.json +1 -1
  152. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/app.controller.spec.ts +0 -22
  153. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/BarChart/index.tsx +0 -896
  154. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/ChartSwitcher/index.tsx +0 -219
  155. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/ChartViewer/index.tsx +0 -159
  156. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Filter/index.tsx +0 -192
  157. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/LineChart/index.tsx +0 -1034
  158. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumCard/NumCard.module.css +0 -8
  159. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumCard/index.tsx +0 -509
  160. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/NumLineCard/index.tsx +0 -66
  161. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/PieChart/index.tsx +0 -552
  162. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/RadarChart/index.tsx +0 -263
  163. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Section/index.tsx +0 -35
  164. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/Table/index.tsx +0 -207
  165. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/TreemapChart/index.tsx +0 -382
  166. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/WorldMap/index.tsx +0 -135
  167. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/chart-constants.ts +0 -53
  168. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/icon/InfoIcon.tsx +0 -8
  169. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/icon/index.ts +0 -1
  170. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/config.ts +0 -31
  171. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/nameMap.json +0 -9
  172. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/map/world.geo.json +0 -39349
  173. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/canvas/metric-info-tooltip/index.tsx +0 -19
@@ -0,0 +1,566 @@
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import {
9
+ Button,
10
+ Card,
11
+ Input,
12
+ message,
13
+ Modal,
14
+ Select,
15
+ Space,
16
+ Spin,
17
+ Tag,
18
+ Typography,
19
+ } from 'antd';
20
+ import {
21
+ ArrowLeftOutlined,
22
+ EditOutlined,
23
+ RobotOutlined,
24
+ RocketOutlined,
25
+ SaveOutlined,
26
+ } from '@ant-design/icons';
27
+ import { useNavigate, useParams } from 'react-router-dom';
28
+ import { customRequest } from 'helpers/http';
29
+ import { FlowRenderer } from './components/FlowRenderer';
30
+ import { NodePropertyPanel } from './components/NodePropertyPanel';
31
+ import { useWorkflowAgent } from './hooks/useWorkflowAgent';
32
+ import type {
33
+ Workflow,
34
+ WorkflowDSL,
35
+ WorkflowNode,
36
+ WorkflowNodeType,
37
+ WorkflowVersion,
38
+ } from './types';
39
+
40
+ const { Title } = Typography;
41
+ const { TextArea } = Input;
42
+
43
+ const NEW_DRAFT_KEY = 'workflow_new_draft';
44
+ const EDIT_DRAFT_KEY = (id: string) => `workflow_draft_${id}`;
45
+ const isDev = import.meta.env.DEV;
46
+
47
+ export default function WorkflowEditorPage() {
48
+ const { id } = useParams<{ id: string }>();
49
+ const isNewMode = !id;
50
+ const navigate = useNavigate();
51
+
52
+ const [workflow, setWorkflow] = useState<Workflow | null>(null);
53
+ const [versions, setVersions] = useState<WorkflowVersion[]>([]);
54
+ const [nodeTypes, setNodeTypes] = useState<WorkflowNodeType[]>([]);
55
+ const [loading, setLoading] = useState(!isNewMode);
56
+
57
+ const [dsl, setDsl] = useState<WorkflowDSL | null>(null);
58
+ const [currentVersion, setCurrentVersion] = useState<number | null>(null);
59
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
60
+
61
+ const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
62
+
63
+ // Title editing state
64
+ const [title, setTitle] = useState(isNewMode ? 'Untitled Workflow' : '');
65
+ const [editingTitle, setEditingTitle] = useState(false);
66
+ const titleInputRef = useRef<any>(null);
67
+
68
+ // Save name modal
69
+ const [nameModalOpen, setNameModalOpen] = useState(false);
70
+ const [saveName, setSaveName] = useState('');
71
+ const [saving, setSaving] = useState(false);
72
+
73
+ // AI generate modal
74
+ const [aiModalOpen, setAiModalOpen] = useState(false);
75
+ const [aiPromptText, setAiPromptText] = useState('');
76
+
77
+ const nodeTypeMap = useMemo(() => {
78
+ const map: Record<string, { category: string; icon: string }> = {};
79
+ for (const nt of nodeTypes) {
80
+ map[nt.type] = { category: nt.category, icon: nt.icon };
81
+ }
82
+ return map;
83
+ }, [nodeTypes]);
84
+
85
+ const selectedNode: WorkflowNode | null = useMemo(() => {
86
+ if (!selectedNodeId || !dsl) return null;
87
+ return dsl.nodes.find((n) => n.id === selectedNodeId) || null;
88
+ }, [selectedNodeId, dsl]);
89
+
90
+ const {
91
+ generate,
92
+ abort: abortAgent,
93
+ loading: agentLoading,
94
+ error: agentError,
95
+ } = useWorkflowAgent({ nodeTypes });
96
+
97
+ // --- Load logic ---
98
+ useEffect(() => {
99
+ // Load node types for both modes
100
+ customRequest<WorkflowNodeType[]>('workflow-node-types', 'GET')
101
+ .then(setNodeTypes)
102
+ .catch(() => {});
103
+ }, []);
104
+
105
+ useEffect(() => {
106
+ if (isNewMode) {
107
+ // New mode: load from localStorage
108
+ const saved = localStorage.getItem(NEW_DRAFT_KEY);
109
+ if (saved) {
110
+ try {
111
+ const parsed = JSON.parse(saved);
112
+ setDsl(parsed.dsl || null);
113
+ setTitle(parsed.title || 'Untitled Workflow');
114
+ } catch {
115
+ localStorage.removeItem(NEW_DRAFT_KEY);
116
+ }
117
+ }
118
+ return;
119
+ }
120
+
121
+ // Edit mode: load from DB
122
+ setLoading(true);
123
+ setHasUnsavedChanges(false);
124
+ Promise.all([
125
+ customRequest<Workflow>(`workflow/${id}`, 'GET'),
126
+ customRequest<WorkflowVersion[]>(`workflow/${id}/versions`, 'GET'),
127
+ ])
128
+ .then(([wf, vers]) => {
129
+ setWorkflow(wf);
130
+ setVersions(vers);
131
+ setTitle(wf.name);
132
+ // DSL comes from findOne (includes full latest version with dsl field)
133
+ // getVersions only returns metadata (no dsl) for the version selector
134
+ const latestVersion = (wf as any).versions?.[0];
135
+ if (latestVersion?.dsl) {
136
+ setDsl(latestVersion.dsl);
137
+ setCurrentVersion(latestVersion.version);
138
+ }
139
+ })
140
+ .catch((e) => {
141
+ message.error(e?.message ?? 'Failed to load workflow');
142
+ })
143
+ .finally(() => {
144
+ setLoading(false);
145
+ });
146
+ }, [id, isNewMode]);
147
+
148
+ // --- Edit buffer: write to localStorage on every change ---
149
+ useEffect(() => {
150
+ if (!hasUnsavedChanges) return;
151
+ if (isNewMode) {
152
+ localStorage.setItem(NEW_DRAFT_KEY, JSON.stringify({ dsl, title }));
153
+ } else if (id && dsl) {
154
+ localStorage.setItem(EDIT_DRAFT_KEY(id), JSON.stringify(dsl));
155
+ }
156
+ }, [dsl, title, hasUnsavedChanges, isNewMode, id]);
157
+
158
+ // --- Canvas edit callback ---
159
+ const handleDslChange = useCallback((newDsl: WorkflowDSL) => {
160
+ setDsl(newDsl);
161
+ setHasUnsavedChanges(true);
162
+ }, []);
163
+
164
+ // --- AI edit workflow ---
165
+ const handlePromptSubmit = useCallback(
166
+ async (promptText: string) => {
167
+ if (!promptText.trim()) return;
168
+ const result = await generate({
169
+ action: dsl ? 'edit_workflow' : 'create',
170
+ prompt: promptText.trim(),
171
+ currentDsl: dsl || undefined,
172
+ });
173
+ if (result && 'dsl' in result) {
174
+ setDsl(result.dsl);
175
+ setHasUnsavedChanges(true);
176
+ message.success(result.changeSummary || 'Workflow updated');
177
+ }
178
+ },
179
+ [dsl, generate],
180
+ );
181
+
182
+ // --- AI edit single node ---
183
+ const handleNodeAiEdit = useCallback(
184
+ async (nodeId: string, nodePrompt: string) => {
185
+ if (!dsl) return;
186
+ const result = await generate({
187
+ action: 'edit_node',
188
+ prompt: nodePrompt,
189
+ currentDsl: dsl,
190
+ targetNodeId: nodeId,
191
+ });
192
+ if (result && 'dsl' in result) {
193
+ setDsl(result.dsl);
194
+ setHasUnsavedChanges(true);
195
+ message.success(result.changeSummary || 'Node updated');
196
+ }
197
+ },
198
+ [dsl, generate],
199
+ );
200
+
201
+ // --- Node label change ---
202
+ const handleNodeLabelChange = useCallback(
203
+ (nodeId: string, newLabel: string) => {
204
+ if (!dsl) return;
205
+ const newNodes = dsl.nodes.map((n) =>
206
+ n.id === nodeId ? { ...n, label: newLabel } : n,
207
+ );
208
+ setDsl({ ...dsl, nodes: newNodes });
209
+ setHasUnsavedChanges(true);
210
+ },
211
+ [dsl],
212
+ );
213
+
214
+ // --- Delete node ---
215
+ const handleNodeDelete = useCallback(
216
+ (nodeId: string) => {
217
+ if (!dsl) return;
218
+ const newNodes = dsl.nodes.filter((n) => n.id !== nodeId);
219
+ const newEdges = dsl.edges.filter(
220
+ (e) => e.source !== nodeId && e.target !== nodeId,
221
+ );
222
+ setDsl({ nodes: newNodes, edges: newEdges });
223
+ setHasUnsavedChanges(true);
224
+ setSelectedNodeId(null);
225
+ },
226
+ [dsl],
227
+ );
228
+
229
+ // --- Node property save ---
230
+ const handleNodePropertySave = useCallback(
231
+ (
232
+ nodeId: string,
233
+ updates: {
234
+ label?: string;
235
+ config?: Record<string, any>;
236
+ instanceRef?: WorkflowNode['instanceRef'] | null;
237
+ },
238
+ ) => {
239
+ if (!dsl) return;
240
+ const newNodes = dsl.nodes.map((n) => {
241
+ if (n.id !== nodeId) return n;
242
+ const updated = {
243
+ ...n,
244
+ label: updates.label ?? n.label,
245
+ config: updates.config ?? n.config,
246
+ };
247
+ if (updates.instanceRef === null) {
248
+ delete updated.instanceRef;
249
+ } else if (updates.instanceRef !== undefined) {
250
+ updated.instanceRef = updates.instanceRef;
251
+ }
252
+ return updated;
253
+ });
254
+ setDsl({ ...dsl, nodes: newNodes });
255
+ setHasUnsavedChanges(true);
256
+ message.success('节点属性已更新');
257
+ },
258
+ [dsl],
259
+ );
260
+
261
+ // --- Title editing ---
262
+ function handleTitleEditStart() {
263
+ setEditingTitle(true);
264
+ setTimeout(() => titleInputRef.current?.focus(), 0);
265
+ }
266
+
267
+ function handleTitleEditEnd() {
268
+ setEditingTitle(false);
269
+ const trimmed = title.trim();
270
+ if (!trimmed) {
271
+ setTitle('Untitled Workflow');
272
+ }
273
+ setHasUnsavedChanges(true);
274
+ }
275
+
276
+ // --- Save logic ---
277
+ async function handleSave() {
278
+ if (!dsl) return;
279
+
280
+ if (title === 'Untitled Workflow') {
281
+ setSaveName('');
282
+ setNameModalOpen(true);
283
+ return;
284
+ }
285
+
286
+ await doSave(title);
287
+ }
288
+
289
+ async function handleNameModalOk() {
290
+ const name = saveName.trim();
291
+ if (!name) {
292
+ message.warning('Please enter a workflow name');
293
+ return;
294
+ }
295
+ setTitle(name);
296
+ setNameModalOpen(false);
297
+ await doSave(name);
298
+ }
299
+
300
+ async function doSave(name: string) {
301
+ if (!dsl) return;
302
+ setSaving(true);
303
+ try {
304
+ if (isNewMode) {
305
+ // Create new workflow with DSL
306
+ const wf = await customRequest<{ id: number }>('workflow', 'POST', {
307
+ name,
308
+ description: '',
309
+ dsl,
310
+ });
311
+ localStorage.removeItem(NEW_DRAFT_KEY);
312
+ message.success('Workflow saved!');
313
+ navigate(`/admin/workflow/edit/${wf.id}`, { replace: true });
314
+ } else {
315
+ // Update existing: save draft
316
+ // If name changed from DB, patch it
317
+ if (workflow && name !== workflow.name) {
318
+ await customRequest(`workflow/${id}`, 'PATCH', { name });
319
+ setWorkflow((prev) => (prev ? { ...prev, name } : prev));
320
+ }
321
+ await customRequest(`workflow/${id}/save`, 'PATCH', { dsl });
322
+ localStorage.removeItem(EDIT_DRAFT_KEY(id!));
323
+ setHasUnsavedChanges(false);
324
+ message.success('Saved!');
325
+ }
326
+ } catch (e: any) {
327
+ message.error(e?.message ?? 'Save failed');
328
+ } finally {
329
+ setSaving(false);
330
+ }
331
+ }
332
+
333
+ // --- Version selector ---
334
+ async function handleSelectVersion(version: number) {
335
+ if (!id) return;
336
+
337
+ if (hasUnsavedChanges) {
338
+ Modal.confirm({
339
+ title: '未保存的修改',
340
+ content: '当前有未保存的修改,切换版本将丢失这些更改。确定要继续吗?',
341
+ okText: '确定切换',
342
+ cancelText: '取消',
343
+ onOk: () => doLoadVersion(version),
344
+ });
345
+ return;
346
+ }
347
+
348
+ await doLoadVersion(version);
349
+ }
350
+
351
+ async function doLoadVersion(version: number) {
352
+ if (!id) return;
353
+ try {
354
+ const v = await customRequest<WorkflowVersion>(
355
+ `workflow/${id}/versions/${version}`,
356
+ 'GET',
357
+ );
358
+ setDsl(v.dsl);
359
+ setCurrentVersion(v.version);
360
+ setHasUnsavedChanges(false);
361
+ setSelectedNodeId(null);
362
+ localStorage.removeItem(EDIT_DRAFT_KEY(id));
363
+ } catch (e: any) {
364
+ message.error(e?.message ?? 'Failed to load version');
365
+ }
366
+ }
367
+
368
+ function handleNodeClick(nodeId: string) {
369
+ setSelectedNodeId(nodeId || null);
370
+ }
371
+
372
+ if (loading) {
373
+ return (
374
+ <div
375
+ style={{
376
+ display: 'flex',
377
+ justifyContent: 'center',
378
+ alignItems: 'center',
379
+ height: '100vh',
380
+ }}
381
+ >
382
+ <Spin size="large" />
383
+ </div>
384
+ );
385
+ }
386
+
387
+ return (
388
+ <div
389
+ style={{
390
+ height: 'calc(100vh - 96px)',
391
+ display: 'flex',
392
+ flexDirection: 'column',
393
+ background: '#f0f2f5',
394
+ overflow: 'hidden',
395
+ }}
396
+ >
397
+ {/* Header */}
398
+ <Card style={{ marginBottom: 16, flexShrink: 0 }}>
399
+ <div
400
+ style={{
401
+ display: 'flex',
402
+ justifyContent: 'space-between',
403
+ alignItems: 'center',
404
+ }}
405
+ >
406
+ <Space>
407
+ <Button
408
+ type="text"
409
+ icon={<ArrowLeftOutlined />}
410
+ onClick={() => navigate('/admin/workflow')}
411
+ >
412
+ Back
413
+ </Button>
414
+ {editingTitle ? (
415
+ <Input
416
+ ref={titleInputRef}
417
+ value={title}
418
+ onChange={(e) => setTitle(e.target.value)}
419
+ onBlur={handleTitleEditEnd}
420
+ onPressEnter={handleTitleEditEnd}
421
+ style={{ width: 260, fontWeight: 600, fontSize: 18 }}
422
+ />
423
+ ) : (
424
+ <>
425
+ <Title level={4} style={{ margin: 0 }}>
426
+ {title}
427
+ </Title>
428
+ <Button
429
+ type="text"
430
+ size="small"
431
+ icon={<EditOutlined />}
432
+ onClick={handleTitleEditStart}
433
+ />
434
+ </>
435
+ )}
436
+ {hasUnsavedChanges && <Tag color="orange">Unsaved</Tag>}
437
+ </Space>
438
+ <Space>
439
+ {!isNewMode && versions.length > 0 && (
440
+ <Select
441
+ value={currentVersion}
442
+ onChange={handleSelectVersion}
443
+ style={{ width: 200 }}
444
+ placeholder="选择版本"
445
+ options={versions.map((v) => ({
446
+ label: `v${v.version} - ${v.changeSummary?.slice(0, 20) || 'Saved'}`,
447
+ value: v.version,
448
+ }))}
449
+ />
450
+ )}
451
+ {isDev && (
452
+ <Button
453
+ type="primary"
454
+ icon={<RobotOutlined />}
455
+ onClick={() => setAiModalOpen(true)}
456
+ >
457
+ AI自动生成
458
+ </Button>
459
+ )}
460
+ <Button
461
+ type="primary"
462
+ icon={<SaveOutlined />}
463
+ disabled={!dsl || !hasUnsavedChanges}
464
+ loading={saving}
465
+ onClick={handleSave}
466
+ >
467
+ Save
468
+ </Button>
469
+ </Space>
470
+ </div>
471
+ </Card>
472
+
473
+ {/* Main area: Flow + Property Panel */}
474
+ <div
475
+ style={{ flex: 1, display: 'flex', overflow: 'hidden', minHeight: 0 }}
476
+ >
477
+ <div style={{ flex: 1, position: 'relative' }}>
478
+ <FlowRenderer
479
+ dsl={dsl}
480
+ nodeTypeMap={nodeTypeMap}
481
+ selectedNodeId={selectedNodeId}
482
+ onNodeClick={handleNodeClick}
483
+ onDslChange={handleDslChange}
484
+ />
485
+ </div>
486
+ <NodePropertyPanel
487
+ node={selectedNode}
488
+ nodeTypes={nodeTypes}
489
+ currentWorkflowId={id}
490
+ onSave={handleNodePropertySave}
491
+ onLabelChange={handleNodeLabelChange}
492
+ onAiEdit={handleNodeAiEdit}
493
+ onDelete={handleNodeDelete}
494
+ onAbort={abortAgent}
495
+ aiLoading={agentLoading}
496
+ />
497
+ </div>
498
+
499
+ {/* Name modal for untitled workflows */}
500
+ <Modal
501
+ title="Save Workflow"
502
+ open={nameModalOpen}
503
+ onOk={handleNameModalOk}
504
+ onCancel={() => setNameModalOpen(false)}
505
+ okText="Save"
506
+ okButtonProps={{ loading: saving }}
507
+ >
508
+ <div style={{ marginBottom: 8 }}>
509
+ <Typography.Text strong>Workflow Name</Typography.Text>
510
+ </div>
511
+ <Input
512
+ placeholder="Enter workflow name"
513
+ value={saveName}
514
+ onChange={(e) => setSaveName(e.target.value)}
515
+ onPressEnter={handleNameModalOk}
516
+ autoFocus
517
+ />
518
+ </Modal>
519
+
520
+ {/* AI Generate modal */}
521
+ <Modal
522
+ title={null}
523
+ open={aiModalOpen}
524
+ onCancel={() => setAiModalOpen(false)}
525
+ footer={null}
526
+ width={520}
527
+ >
528
+ <div style={{ marginBottom: 12 }}>
529
+ <Typography.Text style={{ fontSize: 14 }}>
530
+ 用自然语言描述整个工作流,AI将自动生成可视化的流程
531
+ </Typography.Text>
532
+ </div>
533
+ <TextArea
534
+ value={aiPromptText}
535
+ onChange={(e) => setAiPromptText(e.target.value)}
536
+ placeholder="Describe changes to this workflow..."
537
+ autoSize={{ minRows: 6, maxRows: 12 }}
538
+ onPressEnter={(e) => {
539
+ if (e.ctrlKey && aiPromptText.trim()) {
540
+ handlePromptSubmit(aiPromptText.trim());
541
+ setAiPromptText('');
542
+ setAiModalOpen(false);
543
+ }
544
+ }}
545
+ />
546
+ <Button
547
+ type="primary"
548
+ icon={<RocketOutlined />}
549
+ onClick={() => {
550
+ if (aiPromptText.trim()) {
551
+ handlePromptSubmit(aiPromptText.trim());
552
+ setAiPromptText('');
553
+ setAiModalOpen(false);
554
+ }
555
+ }}
556
+ disabled={!aiPromptText.trim()}
557
+ loading={agentLoading}
558
+ style={{ marginTop: 12 }}
559
+ block
560
+ >
561
+ Generate Workflow
562
+ </Button>
563
+ </Modal>
564
+ </div>
565
+ );
566
+ }