@gadmin2n/schematics 0.0.72 → 0.0.75

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,322 @@
1
+ import React, { useCallback, useLayoutEffect, useMemo, useRef } from 'react';
2
+ import {
3
+ ReactFlow,
4
+ ReactFlowProvider,
5
+ Background,
6
+ Controls,
7
+ MiniMap,
8
+ ConnectionMode,
9
+ useReactFlow,
10
+ type Node,
11
+ type Edge,
12
+ type Connection,
13
+ type NodeChange,
14
+ type EdgeChange,
15
+ applyNodeChanges,
16
+ } from '@xyflow/react';
17
+ import '@xyflow/react/dist/style.css';
18
+ import { CustomNode } from './CustomNode';
19
+ import type { WorkflowDSL, WorkflowNodeType } from '../types';
20
+
21
+ const nodeTypes = { custom: CustomNode };
22
+
23
+ interface Props {
24
+ dsl: WorkflowDSL | null;
25
+ nodeTypeMap?: Record<string, { category: string; icon: string }>;
26
+ selectedNodeId?: string | null;
27
+ readonly?: boolean;
28
+ onNodeClick?: (nodeId: string) => void;
29
+ onDslChange?: (dsl: WorkflowDSL) => void;
30
+ }
31
+
32
+ function FlowCanvas({
33
+ dsl,
34
+ nodeTypeMap,
35
+ selectedNodeId,
36
+ readonly,
37
+ onNodeClick,
38
+ onDslChange,
39
+ }: Props) {
40
+ const reactFlowInstance = useReactFlow();
41
+ const dslRef = useRef(dsl);
42
+ dslRef.current = dsl;
43
+ const containerRef = useRef<HTMLDivElement>(null);
44
+ const hasSetViewport = useRef(false);
45
+
46
+ const { nodes, edges } = useMemo(() => {
47
+ if (!dsl) return { nodes: [] as Node[], edges: [] as Edge[] };
48
+
49
+ const rfNodes: Node[] = dsl.nodes.map((n) => {
50
+ const category = nodeTypeMap?.[n.type]?.category || 'ACTION';
51
+ // Transpose x/y for vertical (top-to-bottom) layout
52
+ // Scale y down to compress vertical spacing (original x-step=250 → 120)
53
+ // Add y offset so top node isn't clipped
54
+ const position = { x: n.position.y * 1.5, y: n.position.x * 0.5 + 20 };
55
+ return {
56
+ id: n.id,
57
+ type: 'custom',
58
+ position,
59
+ measured: { width: 140, height: 40 },
60
+ selected: n.id === selectedNodeId,
61
+ data: {
62
+ label: n.label,
63
+ category,
64
+ isSelected: n.id === selectedNodeId,
65
+ readonly: !!readonly,
66
+ },
67
+ };
68
+ });
69
+
70
+ const rfEdges: Edge[] = dsl.edges.map((e) => ({
71
+ id: e.id,
72
+ source: e.source,
73
+ target: e.target,
74
+ sourceHandle: e.sourceHandle,
75
+ label: e.label,
76
+ type: 'smoothstep',
77
+ animated: true,
78
+ }));
79
+
80
+ return { nodes: rfNodes, edges: rfEdges };
81
+ }, [dsl, nodeTypeMap, selectedNodeId]);
82
+
83
+ // Set viewport once before first paint: horizontal center + vertical top padding
84
+ useLayoutEffect(() => {
85
+ if (!hasSetViewport.current && containerRef.current && nodes.length > 0) {
86
+ const containerWidth = containerRef.current.clientWidth;
87
+ let minX = Infinity,
88
+ maxX = -Infinity;
89
+ nodes.forEach((n) => {
90
+ const w = n.measured?.width || 140;
91
+ minX = Math.min(minX, n.position.x);
92
+ maxX = Math.max(maxX, n.position.x + w);
93
+ });
94
+ const contentWidth = maxX - minX;
95
+ const x = (containerWidth - contentWidth) / 2 - minX;
96
+ reactFlowInstance.setViewport({ x, y: 0, zoom: 1 });
97
+ hasSetViewport.current = true;
98
+ }
99
+ }, [nodes, reactFlowInstance]);
100
+
101
+ const onNodesChange = useCallback(
102
+ (changes: NodeChange[]) => {
103
+ const currentDsl = dslRef.current;
104
+ if (!currentDsl || !onDslChange) return;
105
+
106
+ // Apply changes to get new node positions
107
+ const updatedRfNodes = applyNodeChanges(changes, nodes);
108
+
109
+ // Check if any position actually changed
110
+ const positionChanged = changes.some(
111
+ (c) => c.type === 'position' && c.position,
112
+ );
113
+ const removeChanges = changes.filter((c) => c.type === 'remove');
114
+
115
+ if (removeChanges.length > 0) {
116
+ const removedIds = new Set(
117
+ removeChanges.map((c) => (c as any).id as string),
118
+ );
119
+ const newNodes = currentDsl.nodes.filter((n) => !removedIds.has(n.id));
120
+ const newEdges = currentDsl.edges.filter(
121
+ (e) => !removedIds.has(e.source) && !removedIds.has(e.target),
122
+ );
123
+ onDslChange({ nodes: newNodes, edges: newEdges });
124
+ } else if (positionChanged) {
125
+ const newNodes = currentDsl.nodes.map((n) => {
126
+ const rfNode = updatedRfNodes.find((rn) => rn.id === n.id);
127
+ if (rfNode && rfNode.position) {
128
+ // Reverse transpose: display (x,y) → stored (y,x) with scale reversal
129
+ return {
130
+ ...n,
131
+ position: {
132
+ x: (rfNode.position.y - 20) / 0.5,
133
+ y: rfNode.position.x / 1.5,
134
+ },
135
+ };
136
+ }
137
+ return n;
138
+ });
139
+ onDslChange({ ...currentDsl, nodes: newNodes });
140
+ }
141
+ },
142
+ [nodes, onDslChange],
143
+ );
144
+
145
+ const onEdgesChange = useCallback(
146
+ (changes: EdgeChange[]) => {
147
+ const currentDsl = dslRef.current;
148
+ if (!currentDsl || !onDslChange) return;
149
+
150
+ const removeChanges = changes.filter((c) => c.type === 'remove');
151
+ if (removeChanges.length > 0) {
152
+ const removedIds = new Set(
153
+ removeChanges.map((c) => (c as any).id as string),
154
+ );
155
+ const newEdges = currentDsl.edges.filter((e) => !removedIds.has(e.id));
156
+ onDslChange({ ...currentDsl, edges: newEdges });
157
+ }
158
+ },
159
+ [onDslChange],
160
+ );
161
+
162
+ const onConnect = useCallback(
163
+ (connection: Connection) => {
164
+ const currentDsl = dslRef.current;
165
+ if (!currentDsl || !onDslChange) return;
166
+
167
+ const newEdge = {
168
+ id: `e-${connection.source}-${connection.target}-${Date.now()}`,
169
+ source: connection.source!,
170
+ target: connection.target!,
171
+ sourceHandle: connection.sourceHandle || undefined,
172
+ label: undefined,
173
+ };
174
+ onDslChange({
175
+ ...currentDsl,
176
+ edges: [...currentDsl.edges, newEdge],
177
+ });
178
+ },
179
+ [onDslChange],
180
+ );
181
+
182
+ const onDragOver = useCallback((event: React.DragEvent) => {
183
+ event.preventDefault();
184
+ event.dataTransfer.dropEffect = 'move';
185
+ }, []);
186
+
187
+ const onDrop = useCallback(
188
+ (event: React.DragEvent) => {
189
+ event.preventDefault();
190
+ const currentDsl = dslRef.current;
191
+ if (!onDslChange) return;
192
+
193
+ const data = event.dataTransfer.getData('application/workflow-node-type');
194
+ if (!data) return;
195
+
196
+ let nodeType: WorkflowNodeType;
197
+ try {
198
+ nodeType = JSON.parse(data);
199
+ } catch {
200
+ return;
201
+ }
202
+
203
+ const flowPosition = reactFlowInstance.screenToFlowPosition({
204
+ x: event.clientX,
205
+ y: event.clientY,
206
+ });
207
+ // Reverse transpose: display (x,y) → stored (y,x) with scale reversal
208
+ const position = {
209
+ x: (flowPosition.y - 20) / 0.5,
210
+ y: flowPosition.x / 1.5,
211
+ };
212
+
213
+ // Initialize config with default values from configSchema
214
+ const initialConfig: Record<string, any> = {};
215
+ const schema = nodeType.configSchema as any;
216
+ if (schema?.properties) {
217
+ for (const [key, prop] of Object.entries<any>(schema.properties)) {
218
+ if (prop.type === 'string') initialConfig[key] = '';
219
+ else if (prop.type === 'number') initialConfig[key] = undefined;
220
+ else if (prop.type === 'boolean') initialConfig[key] = false;
221
+ else if (prop.type === 'array') initialConfig[key] = [];
222
+ else if (prop.type === 'object') initialConfig[key] = {};
223
+ }
224
+ }
225
+
226
+ const newNode = {
227
+ id: crypto.randomUUID(),
228
+ type: nodeType.type,
229
+ label: nodeType.label,
230
+ position,
231
+ config: initialConfig,
232
+ };
233
+
234
+ const newDsl: WorkflowDSL = currentDsl
235
+ ? { ...currentDsl, nodes: [...currentDsl.nodes, newNode] }
236
+ : { nodes: [newNode], edges: [] };
237
+
238
+ onDslChange(newDsl);
239
+ },
240
+ [reactFlowInstance, onDslChange],
241
+ );
242
+
243
+ if (!dsl || dsl.nodes.length === 0) {
244
+ return (
245
+ <div
246
+ style={{ width: '100%', height: '100%' }}
247
+ onDragOver={onDragOver}
248
+ onDrop={onDrop}
249
+ >
250
+ <ReactFlow
251
+ nodes={[]}
252
+ edges={[]}
253
+ nodeTypes={nodeTypes}
254
+ fitView
255
+ onDragOver={onDragOver}
256
+ onDrop={onDrop}
257
+ >
258
+ <Background />
259
+ <Controls showInteractive={false} />
260
+ </ReactFlow>
261
+ </div>
262
+ );
263
+ }
264
+
265
+ return (
266
+ <div ref={containerRef} style={{ width: '100%', height: '100%' }}>
267
+ <ReactFlow
268
+ nodes={nodes}
269
+ edges={edges}
270
+ nodeTypes={nodeTypes}
271
+ onNodesChange={onNodesChange}
272
+ onEdgesChange={onEdgesChange}
273
+ onConnect={onConnect}
274
+ onDragOver={onDragOver}
275
+ onDrop={onDrop}
276
+ nodesDraggable={true}
277
+ nodesConnectable={true}
278
+ elementsSelectable={true}
279
+ connectionMode={ConnectionMode.Loose}
280
+ deleteKeyCode={['Backspace', 'Delete']}
281
+ zoomOnScroll={false}
282
+ zoomOnPinch={false}
283
+ zoomOnDoubleClick={false}
284
+ onNodeClick={(_event, node) => {
285
+ onNodeClick?.(node.id);
286
+ }}
287
+ onPaneClick={() => {
288
+ onNodeClick?.('');
289
+ }}
290
+ >
291
+ <Background />
292
+ <Controls showInteractive={false} />
293
+ <MiniMap
294
+ nodeColor={(node) => {
295
+ const category = (node.data as any)?.category || 'ACTION';
296
+ const colors: Record<string, string> = {
297
+ TRIGGER: '#91caff',
298
+ ACTION: '#b7eb8f',
299
+ CONDITION: '#ffd591',
300
+ LOOP: '#d3adf7',
301
+ APPROVAL: '#ffa39e',
302
+ SUB_WORKFLOW: '#adc6ff',
303
+ };
304
+ return colors[category] || '#b7eb8f';
305
+ }}
306
+ nodeStrokeWidth={3}
307
+ maskColor="rgba(240, 240, 240, 0.6)"
308
+ zoomable
309
+ pannable
310
+ />
311
+ </ReactFlow>
312
+ </div>
313
+ );
314
+ }
315
+
316
+ export function FlowRenderer(props: Props) {
317
+ return (
318
+ <ReactFlowProvider>
319
+ <FlowCanvas {...props} />
320
+ </ReactFlowProvider>
321
+ );
322
+ }
@@ -0,0 +1,175 @@
1
+ import React, { useCallback, useState } from 'react';
2
+ import { Button, Input, Modal, Space, Spin, message } from 'antd';
3
+ import { CloudDownloadOutlined, CheckOutlined } from '@ant-design/icons';
4
+ import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer-continued';
5
+ import { customRequest } from 'helpers/http';
6
+
7
+ interface ImportModalProps {
8
+ open: boolean;
9
+ onClose: () => void;
10
+ onSuccess: () => void;
11
+ }
12
+
13
+ type Phase = 'input' | 'preview' | 'confirming';
14
+
15
+ export function ImportModal({ open, onClose, onSuccess }: ImportModalProps) {
16
+ const [sourceUrl, setSourceUrl] = useState('');
17
+ const [phase, setPhase] = useState<Phase>('input');
18
+ const [loading, setLoading] = useState(false);
19
+ const [currentJson, setCurrentJson] = useState('');
20
+ const [incomingJson, setIncomingJson] = useState('');
21
+ const [payload, setPayload] = useState<any>(null);
22
+
23
+ const reset = useCallback(() => {
24
+ setSourceUrl('');
25
+ setPhase('input');
26
+ setLoading(false);
27
+ setCurrentJson('');
28
+ setIncomingJson('');
29
+ setPayload(null);
30
+ }, []);
31
+
32
+ const handleClose = useCallback(() => {
33
+ reset();
34
+ onClose();
35
+ }, [reset, onClose]);
36
+
37
+ const handleFetchPreview = useCallback(async () => {
38
+ if (!sourceUrl.trim()) {
39
+ message.warning('Please paste the export URL');
40
+ return;
41
+ }
42
+
43
+ setLoading(true);
44
+ try {
45
+ // Step 1: Fetch from source URL (cross-origin)
46
+ const response = await fetch(sourceUrl.trim());
47
+ if (!response.ok) {
48
+ const errorData = await response.json().catch(() => null);
49
+ throw new Error(
50
+ errorData?.message || `Failed to fetch: ${response.status}`,
51
+ );
52
+ }
53
+ const exportData = await response.json();
54
+
55
+ // Step 2: Send to our import-preview endpoint
56
+ const preview = await customRequest<{
57
+ current: any;
58
+ incoming: any;
59
+ }>('workflow/import-preview', 'POST', { payload: exportData });
60
+
61
+ // Step 3: Format for diff viewer
62
+ setCurrentJson(
63
+ preview.current ? JSON.stringify(preview.current, null, 2) : '',
64
+ );
65
+ setIncomingJson(JSON.stringify(preview.incoming, null, 2));
66
+ setPayload(exportData);
67
+ setPhase('preview');
68
+ } catch (e: any) {
69
+ if (e.message?.includes('expired') || e.message?.includes('invalid')) {
70
+ message.error(
71
+ 'Export link has expired or is invalid. Please generate a new one from the source environment.',
72
+ );
73
+ } else {
74
+ message.error(
75
+ e?.message ||
76
+ 'Cannot reach the source environment. Please check the URL and try again.',
77
+ );
78
+ }
79
+ } finally {
80
+ setLoading(false);
81
+ }
82
+ }, [sourceUrl]);
83
+
84
+ const handleConfirm = useCallback(async () => {
85
+ if (!payload) return;
86
+ setPhase('confirming');
87
+ try {
88
+ const result = await customRequest<{
89
+ success: boolean;
90
+ workflowId: string;
91
+ workflowName: string;
92
+ }>('workflow/import-confirm', 'POST', { payload });
93
+
94
+ message.success(
95
+ `Workflow "${result.workflowName}" imported successfully!`,
96
+ );
97
+ reset();
98
+ onClose();
99
+ onSuccess();
100
+ } catch (e: any) {
101
+ message.error(e?.message ?? 'Import failed');
102
+ setPhase('preview');
103
+ }
104
+ }, [payload, reset, onClose, onSuccess]);
105
+
106
+ return (
107
+ <Modal
108
+ title="Import Workflow"
109
+ open={open}
110
+ onCancel={handleClose}
111
+ width={phase === 'preview' ? '90vw' : 520}
112
+ footer={
113
+ phase === 'input' ? (
114
+ <Space>
115
+ <Button onClick={handleClose}>Cancel</Button>
116
+ <Button
117
+ type="primary"
118
+ icon={<CloudDownloadOutlined />}
119
+ onClick={handleFetchPreview}
120
+ loading={loading}
121
+ >
122
+ Fetch &amp; Preview
123
+ </Button>
124
+ </Space>
125
+ ) : (
126
+ <Space>
127
+ <Button onClick={handleClose}>Cancel</Button>
128
+ <Button
129
+ type="primary"
130
+ icon={<CheckOutlined />}
131
+ onClick={handleConfirm}
132
+ loading={phase === 'confirming'}
133
+ >
134
+ Confirm Import
135
+ </Button>
136
+ </Space>
137
+ )
138
+ }
139
+ >
140
+ {phase === 'input' && (
141
+ <>
142
+ <p style={{ marginBottom: 8 }}>
143
+ Paste the export URL from the source environment:
144
+ </p>
145
+ <Input.TextArea
146
+ value={sourceUrl}
147
+ onChange={(e) => setSourceUrl(e.target.value)}
148
+ placeholder="https://dev.example.com/api/workflow/export/abc123?token=..."
149
+ autoSize={{ minRows: 3, maxRows: 5 }}
150
+ />
151
+ </>
152
+ )}
153
+
154
+ {phase === 'preview' && (
155
+ <div style={{ maxHeight: '70vh', overflow: 'auto' }}>
156
+ <ReactDiffViewer
157
+ oldValue={currentJson}
158
+ newValue={incomingJson}
159
+ splitView
160
+ compareMethod={DiffMethod.LINES}
161
+ leftTitle="Current (this environment)"
162
+ rightTitle="Incoming (source environment)"
163
+ />
164
+ </div>
165
+ )}
166
+
167
+ {phase === 'confirming' && (
168
+ <div style={{ textAlign: 'center', padding: 40 }}>
169
+ <Spin size="large" />
170
+ <p style={{ marginTop: 16 }}>Importing workflow...</p>
171
+ </div>
172
+ )}
173
+ </Modal>
174
+ );
175
+ }
@@ -0,0 +1,60 @@
1
+ import React, { useState } from 'react';
2
+ import { Input, Modal, Space, Typography } from 'antd';
3
+
4
+ const { Text } = Typography;
5
+ const { TextArea } = Input;
6
+
7
+ interface Props {
8
+ open: boolean;
9
+ nodeId: string | null;
10
+ nodeLabel: string | null;
11
+ onClose: () => void;
12
+ onSubmit: (prompt: string) => void;
13
+ loading: boolean;
14
+ }
15
+
16
+ export function NodeEditModal({
17
+ open,
18
+ nodeId,
19
+ nodeLabel,
20
+ onClose,
21
+ onSubmit,
22
+ loading,
23
+ }: Props) {
24
+ const [prompt, setPrompt] = useState('');
25
+
26
+ function handleSubmit() {
27
+ if (!prompt.trim()) return;
28
+ onSubmit(prompt.trim());
29
+ setPrompt('');
30
+ }
31
+
32
+ return (
33
+ <Modal
34
+ title={`Edit Node: ${nodeLabel || nodeId}`}
35
+ open={open}
36
+ onCancel={onClose}
37
+ onOk={handleSubmit}
38
+ okText="Apply"
39
+ okButtonProps={{ loading }}
40
+ destroyOnClose
41
+ width={480}
42
+ >
43
+ <Space direction="vertical" style={{ width: '100%' }} size="middle">
44
+ <Text type="secondary">
45
+ Describe how you want to modify this node. The AI will update only
46
+ this node.
47
+ </Text>
48
+ <TextArea
49
+ rows={3}
50
+ placeholder="e.g. Change the timeout to 30 minutes and add a fallback approver"
51
+ value={prompt}
52
+ onChange={(e) => setPrompt(e.target.value)}
53
+ onPressEnter={(e) => {
54
+ if (e.ctrlKey) handleSubmit();
55
+ }}
56
+ />
57
+ </Space>
58
+ </Modal>
59
+ );
60
+ }