@gadmin2n/schematics 0.0.152 → 0.0.154

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 (110) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/.git-blame-ignore-revs +5 -0
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/.nvmrc +1 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/CODEBUDDY.local.md +8 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/CODEBUDDY.md +4 -4
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.codegen +16 -9
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.server +21 -13
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/Dockerfile.web +11 -11
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/Jenkinsfile +1 -1
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/PRODUCT.md +1 -1
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/compose-ctl.sh +1 -1
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +1 -0
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/docker-compose.yml +4 -4
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/readme.md +22 -22
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.oxfmtrc.json +13 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.oxlintrc.json +17 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.seed-timestamp +1 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +14 -13
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/pnpm-workspace.yaml +23 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/index.ts +2 -1
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permission.seed.ts +23 -44
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +25 -4
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow.seed.ts +74 -64
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/auth.guard.ts +4 -1
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.ts +2 -2
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/tracing.ts +4 -6
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.service.ts +2 -2
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/audit/audit.controller.ts +1 -1
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-runtime.controller.ts +6 -6
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/game/game.controller.ts +1 -1
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/http-request-runtime.controller.ts +6 -6
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/httpRequest/http-request-runtime.util.ts +4 -4
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.ts +1 -1
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.service.ts +4 -4
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.controller.ts +1 -1
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.service.ts +2 -2
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/pageResource/pageResource.controller.ts +1 -1
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/page-transfer.service.ts +1 -1
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.ts +1 -1
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/rolePages/rolePages.controller.ts +1 -1
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/roleResource/roleResource.controller.ts +1 -1
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.service.spec.ts +1 -1
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.errors.ts +4 -1
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.registry.ts +4 -4
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/subject-provider.types.ts +4 -4
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +1 -1
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.ts +2 -2
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/parameter-binding.ts +4 -4
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +0 -7
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-cancellation.spec.ts +17 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +58 -3
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +66 -6
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-data-edge.util.ts +5 -5
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution-idempotency.spec.ts +61 -149
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution.dto.ts +1 -1
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-handle-validator.ts +2 -2
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-runtime.controller.spec.ts +1 -1
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-runtime.controller.ts +1 -1
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle.service.spec.ts +2 -2
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.spec.ts +38 -0
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +18 -7
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.dto.ts +1 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +142 -253
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.ts +3 -1
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeInstance/workflowNodeInstance.controller.ts +1 -1
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowNodeType/workflowNodeType.controller.ts +1 -1
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/server/start-prod.sh +4 -4
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/web/.oxfmtrc.json +12 -0
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/web/.oxlintrc.json +24 -0
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/web/README.MD +2 -2
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/web/nginx.conf +0 -4
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/web/package.json +17 -17
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/web/pnpm-workspace.yaml +13 -0
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/VanillaJSONEditor/index.jsx +4 -4
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/propPatcher.ts +36 -8
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.test.ts +7 -5
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +6 -36
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/style.css +24 -7
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +0 -4
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +29 -7
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +29 -7
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +2 -2
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/audit/components/action-cell.css +1 -1
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/index.tsx +20 -17
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/index.tsx +38 -35
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.tsx +6 -1
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.tsx +24 -22
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/EnhancedFlowRenderer.tsx +0 -11
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/StatusCards.tsx +24 -0
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.tsx +20 -18
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +7 -0
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +28 -79
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instances.tsx +5 -0
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/NodeInstanceForm.tsx +0 -1
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/DbExecuteConfigEditor.tsx +227 -97
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SimplifiedNodeEditors.test.tsx +62 -0
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/settings/index.tsx +73 -0
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +21 -46
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +2 -0
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +1 -0
  100. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/styles/index.css +1 -1
  101. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/test/setup.ts +1 -1
  102. package/dist/lib/application/files/gadmin2-game-angle-demo/web/vite.config.ts +53 -41
  103. package/dist/lib/application/files/gadmin2-game-angle-demo/web/vitest.config.ts +8 -2
  104. package/package.json +1 -1
  105. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.eslintrc.js +0 -28
  106. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.prettierignore +0 -7
  107. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.prettierrc +0 -4
  108. package/dist/lib/application/files/gadmin2-game-angle-demo/web/.eslintrc.json +0 -38
  109. package/dist/lib/application/files/gadmin2-game-angle-demo/web/.prettierignore +0 -2
  110. package/dist/lib/application/files/gadmin2-game-angle-demo/web/.prettierrc +0 -4
@@ -13,6 +13,7 @@ const PAGE_SIZE = 20;
13
13
  const STATUS_TAG: Record<string, { color: string; label: string }> = {
14
14
  PENDING: { color: 'gold', label: 'Pending' },
15
15
  RUNNING: { color: 'blue', label: 'Running' },
16
+ WAITING_RETRY: { color: 'orange', label: 'Waiting to retry' },
16
17
  COMPLETED: { color: 'green', label: 'Completed' },
17
18
  FAILED: { color: 'red', label: 'Failed' },
18
19
  CANCELLED: { color: 'default', label: 'Cancelled' },
@@ -233,6 +234,10 @@ export default function WorkflowInstancesPage() {
233
234
  options={[
234
235
  { value: 'PENDING', label: t('instances.statuses.pending') },
235
236
  { value: 'RUNNING', label: t('instances.statuses.running') },
237
+ {
238
+ value: 'WAITING_RETRY',
239
+ label: t('instances.statuses.waiting_retry'),
240
+ },
236
241
  { value: 'COMPLETED', label: t('instances.statuses.completed') },
237
242
  { value: 'FAILED', label: t('instances.statuses.failed') },
238
243
  { value: 'CANCELLED', label: t('instances.statuses.cancelled') },
@@ -181,7 +181,6 @@ export function NodeInstanceForm({
181
181
  ]}
182
182
  >
183
183
  <Select
184
- size="large"
185
184
  placeholder={t('nodeInstance.selectNodeType')}
186
185
  disabled={disableNodeType}
187
186
  showSearch
@@ -1,10 +1,22 @@
1
- import { Alert, Button, InputNumber, Select, Space, Typography } from 'antd';
1
+ import {
2
+ Alert,
3
+ Button,
4
+ InputNumber,
5
+ Segmented,
6
+ Select,
7
+ Space,
8
+ Switch,
9
+ Tag,
10
+ Typography,
11
+ } from 'antd';
2
12
  import { customRequest } from 'helpers/http';
3
13
  import { useCallback, useEffect, useMemo, useState } from 'react';
4
14
  import { useTranslation } from 'react-i18next';
5
15
  import { ParameterBindingEditor } from '../../components/ParameterBindingEditor';
16
+ import { WorkflowCodeEditor } from '../../components/WorkflowCodeEditor';
6
17
  import type { ParameterBinding } from '../../types/parameter-binding';
7
18
  import type { WidgetProps } from '../schema-form/widget-registry';
19
+ import { ParameterBindingListEditor } from './ParameterBindingListEditor';
8
20
 
9
21
  export interface DatabaseFunctionArgument {
10
22
  position: number;
@@ -26,11 +38,24 @@ type DatabaseFunctionRef = Pick<
26
38
  >;
27
39
 
28
40
  type DbExecuteConfig = {
41
+ mode?: 'FUNCTION' | 'SQL';
29
42
  functionRef?: DatabaseFunctionRef;
30
43
  argumentBindings?: ParameterBinding[];
44
+ allowWrite?: boolean;
45
+ sql?: string;
46
+ parameters?: ParameterBinding[];
31
47
  timeoutSeconds?: number | null;
32
48
  };
33
49
 
50
+ function executionMode(config: DbExecuteConfig): 'FUNCTION' | 'SQL' {
51
+ if (config.mode === 'FUNCTION' || config.mode === 'SQL') return config.mode;
52
+ return config.sql !== undefined ||
53
+ config.allowWrite !== undefined ||
54
+ config.parameters !== undefined
55
+ ? 'SQL'
56
+ : 'FUNCTION';
57
+ }
58
+
34
59
  function functionKey(value: DatabaseFunctionRef): string {
35
60
  return `${value.schema}.${value.name}(${value.identityArguments})`;
36
61
  }
@@ -50,10 +75,12 @@ export function DbExecuteConfigEditor({
50
75
  }: WidgetProps<DbExecuteConfig | undefined>) {
51
76
  const { t } = useTranslation('workflow');
52
77
  const config = value ?? {};
78
+ const mode = executionMode(config);
79
+ const allowWrite = config.allowWrite === true;
53
80
  const [functions, setFunctions] = useState<DatabaseFunctionOption[]>([]);
54
81
  const [catalogStatus, setCatalogStatus] = useState<
55
- 'loading' | 'ready' | 'error'
56
- >('loading');
82
+ 'idle' | 'loading' | 'ready' | 'error'
83
+ >('idle');
57
84
 
58
85
  const loadFunctions = useCallback(async () => {
59
86
  setCatalogStatus('loading');
@@ -70,8 +97,8 @@ export function DbExecuteConfigEditor({
70
97
  }, []);
71
98
 
72
99
  useEffect(() => {
73
- void loadFunctions();
74
- }, [loadFunctions]);
100
+ if (mode === 'FUNCTION') void loadFunctions();
101
+ }, [loadFunctions, mode]);
75
102
 
76
103
  const selected = useMemo(
77
104
  () =>
@@ -89,109 +116,212 @@ export function DbExecuteConfigEditor({
89
116
  return (
90
117
  <Space direction="vertical" size={14} style={{ width: '100%' }}>
91
118
  <Space direction="vertical" size={6} style={{ width: '100%' }}>
92
- <Typography.Text>{t('nodeEditors.database.function')}</Typography.Text>
93
- <Typography.Text type="secondary">
94
- {t('nodeEditors.database.functionHint')}
95
- </Typography.Text>
96
- {catalogStatus === 'error' && (
97
- <Typography.Text type="danger">
98
- {t('nodeEditors.database.loadFailed')}{' '}
99
- <Button type="link" size="small" onClick={loadFunctions}>
100
- {t('common.retry')}
101
- </Button>
102
- </Typography.Text>
103
- )}
104
- <Select
105
- aria-label={t('nodeEditors.database.function')}
106
- value={selectedKey}
107
- disabled={disabled || catalogStatus !== 'ready'}
108
- loading={catalogStatus === 'loading'}
109
- placeholder={t('nodeEditors.database.selectFunction')}
110
- showSearch
111
- optionFilterProp="label"
112
- options={functions.map((item) => ({
113
- value: functionKey(item),
114
- label: functionKey(item),
115
- }))}
116
- onChange={(key) => {
117
- const databaseFunction = functions.find(
118
- (item) => functionKey(item) === key,
119
- );
120
- if (!databaseFunction) return;
121
- const keepExistingBindings = selectedKey === key;
119
+ <Typography.Text>{t('nodeEditors.database.mode')}</Typography.Text>
120
+ <Segmented
121
+ block
122
+ aria-label={t('nodeEditors.database.mode')}
123
+ value={mode}
124
+ disabled={disabled}
125
+ options={[
126
+ {
127
+ label: t('nodeEditors.database.functionMode'),
128
+ value: 'FUNCTION',
129
+ },
130
+ { label: t('nodeEditors.database.sqlMode'), value: 'SQL' },
131
+ ]}
132
+ onChange={(nextMode) =>
122
133
  onChange({
123
- functionRef: {
124
- schema: databaseFunction.schema,
125
- name: databaseFunction.name,
126
- identityArguments: databaseFunction.identityArguments,
127
- },
128
- argumentBindings: databaseFunction.arguments.map(
129
- (argument, index) =>
130
- (keepExistingBindings
131
- ? config.argumentBindings?.[index]
132
- : undefined) ?? defaultBinding(argument),
133
- ),
134
+ ...config,
135
+ mode: nextMode as 'FUNCTION' | 'SQL',
134
136
  timeoutSeconds: config.timeoutSeconds ?? 30,
135
- });
136
- }}
137
- style={{ width: '100%' }}
137
+ })
138
+ }
138
139
  />
139
140
  </Space>
140
141
 
141
- {catalogStatus === 'ready' && config.functionRef && !selected && (
142
- <Alert
143
- type="warning"
144
- showIcon
145
- message={t('nodeEditors.database.functionMissing')}
146
- />
147
- )}
148
-
149
- {selected && (
142
+ {mode === 'FUNCTION' ? (
150
143
  <>
151
- <Typography.Text type="secondary">
152
- {t('nodeEditors.database.returnType', {
153
- type: selected.returnType,
154
- })}
155
- </Typography.Text>
156
- {selected.arguments.length === 0 ? (
144
+ <Space direction="vertical" size={6} style={{ width: '100%' }}>
145
+ <Typography.Text>
146
+ {t('nodeEditors.database.function')}
147
+ </Typography.Text>
157
148
  <Typography.Text type="secondary">
158
- {t('nodeEditors.database.noArguments')}
149
+ {t('nodeEditors.database.functionHint')}
159
150
  </Typography.Text>
160
- ) : (
161
- selected.arguments.map((argument, index) => (
162
- <Space
163
- key={`${argument.position}:${argument.name}`}
164
- direction="vertical"
165
- size={6}
166
- style={{ width: '100%' }}
167
- >
168
- <Typography.Text>
169
- {argument.name}{' '}
170
- <Typography.Text type="secondary">
171
- ({argument.type})
172
- </Typography.Text>
151
+ {catalogStatus === 'error' && (
152
+ <Typography.Text type="danger">
153
+ {t('nodeEditors.database.loadFailed')}{' '}
154
+ <Button type="link" size="small" onClick={loadFunctions}>
155
+ {t('common.retry')}
156
+ </Button>
157
+ </Typography.Text>
158
+ )}
159
+ <Select
160
+ aria-label={t('nodeEditors.database.function')}
161
+ value={selectedKey}
162
+ disabled={disabled || catalogStatus !== 'ready'}
163
+ loading={catalogStatus === 'loading'}
164
+ placeholder={t('nodeEditors.database.selectFunction')}
165
+ showSearch
166
+ optionFilterProp="label"
167
+ options={functions.map((item) => ({
168
+ value: functionKey(item),
169
+ label: functionKey(item),
170
+ }))}
171
+ onChange={(key) => {
172
+ const databaseFunction = functions.find(
173
+ (item) => functionKey(item) === key,
174
+ );
175
+ if (!databaseFunction) return;
176
+ const keepExistingBindings = selectedKey === key;
177
+ onChange({
178
+ ...config,
179
+ mode: 'FUNCTION',
180
+ functionRef: {
181
+ schema: databaseFunction.schema,
182
+ name: databaseFunction.name,
183
+ identityArguments: databaseFunction.identityArguments,
184
+ },
185
+ argumentBindings: databaseFunction.arguments.map(
186
+ (argument, index) =>
187
+ (keepExistingBindings
188
+ ? config.argumentBindings?.[index]
189
+ : undefined) ?? defaultBinding(argument),
190
+ ),
191
+ timeoutSeconds: config.timeoutSeconds ?? 30,
192
+ });
193
+ }}
194
+ style={{ width: '100%' }}
195
+ />
196
+ </Space>
197
+
198
+ {catalogStatus === 'ready' && config.functionRef && !selected && (
199
+ <Alert
200
+ type="warning"
201
+ showIcon
202
+ message={t('nodeEditors.database.functionMissing')}
203
+ />
204
+ )}
205
+
206
+ {selected && (
207
+ <>
208
+ <Typography.Text type="secondary">
209
+ {t('nodeEditors.database.returnType', {
210
+ type: selected.returnType,
211
+ })}
212
+ </Typography.Text>
213
+ {selected.arguments.length === 0 ? (
214
+ <Typography.Text type="secondary">
215
+ {t('nodeEditors.database.noArguments')}
173
216
  </Typography.Text>
174
- <ParameterBindingEditor
175
- name={argument.name}
176
- defaultPath={defaultArgumentPath(argument.name)}
177
- value={
178
- config.argumentBindings?.[index] ?? defaultBinding(argument)
179
- }
180
- disabled={disabled}
181
- onChange={(binding) => {
182
- const argumentBindings = selected.arguments.map(
183
- (item, bindingIndex) =>
184
- config.argumentBindings?.[bindingIndex] ??
185
- defaultBinding(item),
186
- );
187
- argumentBindings[index] = binding;
188
- onChange({ ...config, argumentBindings });
189
- }}
190
- />
191
- </Space>
192
- ))
217
+ ) : (
218
+ selected.arguments.map((argument, index) => (
219
+ <Space
220
+ key={`${argument.position}:${argument.name}`}
221
+ direction="vertical"
222
+ size={6}
223
+ style={{ width: '100%' }}
224
+ >
225
+ <Typography.Text>
226
+ {argument.name}{' '}
227
+ <Typography.Text type="secondary">
228
+ ({argument.type})
229
+ </Typography.Text>
230
+ </Typography.Text>
231
+ <ParameterBindingEditor
232
+ name={argument.name}
233
+ defaultPath={defaultArgumentPath(argument.name)}
234
+ value={
235
+ config.argumentBindings?.[index] ??
236
+ defaultBinding(argument)
237
+ }
238
+ disabled={disabled}
239
+ onChange={(binding) => {
240
+ const argumentBindings = selected.arguments.map(
241
+ (item, bindingIndex) =>
242
+ config.argumentBindings?.[bindingIndex] ??
243
+ defaultBinding(item),
244
+ );
245
+ argumentBindings[index] = binding;
246
+ onChange({ ...config, argumentBindings });
247
+ }}
248
+ />
249
+ </Space>
250
+ ))
251
+ )}
252
+ </>
193
253
  )}
194
254
  </>
255
+ ) : (
256
+ <>
257
+ <div className="db-execute-config__permission">
258
+ <div>
259
+ <Typography.Text strong>
260
+ {t('nodeEditors.database.allowWrite')}
261
+ </Typography.Text>
262
+ <Typography.Text
263
+ type="secondary"
264
+ className="db-execute-config__hint"
265
+ >
266
+ {t(
267
+ allowWrite
268
+ ? 'nodeEditors.database.readWriteDescription'
269
+ : 'nodeEditors.database.readOnlyDescription',
270
+ )}
271
+ </Typography.Text>
272
+ </div>
273
+ <Switch
274
+ aria-label={t('nodeEditors.database.allowWrite')}
275
+ checked={allowWrite}
276
+ disabled={disabled}
277
+ onChange={(nextAllowWrite) =>
278
+ onChange({ ...config, allowWrite: nextAllowWrite })
279
+ }
280
+ />
281
+ </div>
282
+
283
+ <Space direction="vertical" size={6} style={{ width: '100%' }}>
284
+ <div className="db-execute-config__sql-head">
285
+ <Typography.Text strong>
286
+ {t('nodeEditors.database.sql')}
287
+ </Typography.Text>
288
+ <Tag bordered={false} color={allowWrite ? 'orange' : 'green'}>
289
+ {t(
290
+ allowWrite
291
+ ? 'nodeEditors.database.writeEnabled'
292
+ : 'nodeEditors.database.readOnly',
293
+ )}
294
+ </Tag>
295
+ </div>
296
+ <WorkflowCodeEditor
297
+ language="sql"
298
+ value={config.sql ?? ''}
299
+ readOnly={disabled}
300
+ height={280}
301
+ wordWrap="off"
302
+ ariaLabel={t('nodeEditors.database.sql')}
303
+ onChange={(sql) => onChange({ ...config, sql })}
304
+ />
305
+ </Space>
306
+
307
+ <Space direction="vertical" size={6} style={{ width: '100%' }}>
308
+ <Typography.Text>
309
+ {t('nodeEditors.database.parameters')}
310
+ </Typography.Text>
311
+ <Typography.Text
312
+ type="secondary"
313
+ className="db-execute-config__hint"
314
+ >
315
+ {t('nodeEditors.database.parametersHint')}
316
+ </Typography.Text>
317
+ <ParameterBindingListEditor
318
+ value={config.parameters}
319
+ disabled={disabled}
320
+ addLabel={t('nodeEditors.database.addParameter')}
321
+ onChange={(parameters) => onChange({ ...config, parameters })}
322
+ />
323
+ </Space>
324
+ </>
195
325
  )}
196
326
 
197
327
  <Space direction="vertical" size={6} style={{ width: '100%' }}>
@@ -533,6 +533,7 @@ describe('simplified workflow node editors', () => {
533
533
  );
534
534
 
535
535
  expect(onChange).toHaveBeenCalledWith({
536
+ mode: 'FUNCTION',
536
537
  functionRef: {
537
538
  schema: 'workflow_api',
538
539
  name: 'approve_request',
@@ -549,4 +550,65 @@ describe('simplified workflow node editors', () => {
549
550
  'GET',
550
551
  );
551
552
  });
553
+
554
+ it('switches a database node to Raw SQL without removing its Function config', async () => {
555
+ await i18n.changeLanguage('zh_CN');
556
+ customRequest.mockResolvedValueOnce([]);
557
+ const onChange = vi.fn();
558
+ render(
559
+ <DbExecuteConfigEditor
560
+ value={{
561
+ mode: 'FUNCTION',
562
+ functionRef: {
563
+ schema: 'workflow_api',
564
+ name: 'approve_request',
565
+ identityArguments: 'p_request_id bigint',
566
+ },
567
+ argumentBindings: [{ source: 'INPUT', path: 'request_id' }],
568
+ timeoutSeconds: 30,
569
+ }}
570
+ onChange={onChange}
571
+ schema={schema}
572
+ />,
573
+ );
574
+
575
+ await screen.findByRole('combobox', { name: '数据库 Function' });
576
+ fireEvent.click(screen.getByText('Raw SQL'));
577
+
578
+ expect(onChange).toHaveBeenLastCalledWith({
579
+ mode: 'SQL',
580
+ functionRef: {
581
+ schema: 'workflow_api',
582
+ name: 'approve_request',
583
+ identityArguments: 'p_request_id bigint',
584
+ },
585
+ argumentBindings: [{ source: 'INPUT', path: 'request_id' }],
586
+ timeoutSeconds: 30,
587
+ });
588
+ });
589
+
590
+ it('shows the SQL editor when a database node uses Raw SQL mode', async () => {
591
+ await i18n.changeLanguage('zh_CN');
592
+ customRequest.mockResolvedValueOnce([]);
593
+ render(
594
+ <DbExecuteConfigEditor
595
+ value={{
596
+ mode: 'SQL',
597
+ allowWrite: false,
598
+ sql: 'SELECT id FROM users WHERE id = $1',
599
+ parameters: [{ source: 'INPUT', path: 'id' }],
600
+ timeoutSeconds: 30,
601
+ }}
602
+ onChange={vi.fn()}
603
+ schema={schema}
604
+ />,
605
+ );
606
+
607
+ expect(screen.getByRole('textbox', { name: 'SQL 语句' })).toHaveValue(
608
+ 'SELECT id FROM users WHERE id = $1',
609
+ );
610
+ expect(
611
+ screen.queryByRole('combobox', { name: '数据库 Function' }),
612
+ ).not.toBeInTheDocument();
613
+ });
552
614
  });
@@ -0,0 +1,73 @@
1
+ import { Tabs } from 'antd';
2
+ import { PageHeader } from 'components';
3
+ import { PAGE_WRAPPER_STYLE } from 'constants/layout';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { useSearchParams } from 'react-router-dom';
6
+ import PersonDirectoryPage from 'routes/person-directory';
7
+ import PeopleGroupsPage from 'routes/people-groups';
8
+ import BusinessEventCatalogPage from '../business-events';
9
+ import ConnectionsPage from '../connections';
10
+ import TemporalTenantPage from '../temporal-tenant';
11
+
12
+ const TAB_KEYS = [
13
+ 'tenant',
14
+ 'connections',
15
+ 'events',
16
+ 'people',
17
+ 'groups',
18
+ ] as const;
19
+ type TabKey = (typeof TAB_KEYS)[number];
20
+
21
+ function parseTab(value: string | null): TabKey {
22
+ return (TAB_KEYS as readonly string[]).includes(value ?? '')
23
+ ? (value as TabKey)
24
+ : 'tenant';
25
+ }
26
+
27
+ export default function WorkflowSettingsPage() {
28
+ const { t } = useTranslation('workflow');
29
+ const [searchParams, setSearchParams] = useSearchParams();
30
+ const activeKey = parseTab(searchParams.get('tab'));
31
+
32
+ const items = [
33
+ {
34
+ key: 'tenant',
35
+ label: t('settings.tabTenant'),
36
+ children: <TemporalTenantPage />,
37
+ },
38
+ {
39
+ key: 'connections',
40
+ label: t('settings.tabConnections'),
41
+ children: <ConnectionsPage />,
42
+ },
43
+ {
44
+ key: 'events',
45
+ label: t('settings.tabEvents'),
46
+ children: <BusinessEventCatalogPage />,
47
+ },
48
+ {
49
+ key: 'people',
50
+ label: t('settings.tabPeople'),
51
+ children: <PersonDirectoryPage />,
52
+ },
53
+ {
54
+ key: 'groups',
55
+ label: t('settings.tabGroups'),
56
+ children: <PeopleGroupsPage />,
57
+ },
58
+ ];
59
+
60
+ return (
61
+ <div style={PAGE_WRAPPER_STYLE}>
62
+ <PageHeader
63
+ title={t('settings.title')}
64
+ subtitle={t('settings.subtitle')}
65
+ />
66
+ <Tabs
67
+ activeKey={activeKey}
68
+ items={items}
69
+ onChange={(key) => setSearchParams({ tab: key }, { replace: true })}
70
+ />
71
+ </div>
72
+ );
73
+ }
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  ApiOutlined,
3
- ArrowLeftOutlined,
4
3
  CheckCircleFilled,
5
4
  EditOutlined,
6
5
  EyeInvisibleOutlined,
@@ -27,10 +26,8 @@ import {
27
26
  message,
28
27
  theme,
29
28
  } from 'antd';
30
- import { HEADER_CARD_PROPS, PAGE_WRAPPER_STYLE } from 'constants/layout';
31
29
  import { customRequest } from 'helpers/http';
32
30
  import { useCallback, useEffect, useState } from 'react';
33
- import { useNavigate } from 'react-router-dom';
34
31
  import '../workflow-ui.css';
35
32
  import { useWorkflowFormValidation } from '../useWorkflowFormValidation';
36
33
 
@@ -134,7 +131,6 @@ function SensitiveValue({
134
131
  }
135
132
 
136
133
  export default function TemporalTenantPage() {
137
- const navigate = useNavigate();
138
134
  const [form] = Form.useForm<TenantConfigFormValues>();
139
135
  const formValidation = useWorkflowFormValidation();
140
136
  const { token } = theme.useToken();
@@ -281,55 +277,34 @@ export default function TemporalTenantPage() {
281
277
  }
282
278
 
283
279
  return (
284
- <div className="workflow-page-stack" style={PAGE_WRAPPER_STYLE}>
285
- <Card {...HEADER_CARD_PROPS}>
280
+ <div className="workflow-page-stack">
281
+ {!loading && !loadError && existing && (
286
282
  <div
287
283
  style={{
288
284
  display: 'flex',
289
- alignItems: 'center',
290
- justifyContent: 'space-between',
291
- gap: 16,
292
- flexWrap: 'wrap',
285
+ justifyContent: 'flex-end',
293
286
  }}
294
287
  >
295
- <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
288
+ <Space>
296
289
  <Button
297
- type="text"
298
- icon={<ArrowLeftOutlined />}
299
- onClick={() => navigate(-1)}
300
- />
301
- <div>
302
- <Title level={4} style={{ margin: 0 }}>
303
- 服务注册
304
- </Title>
305
- <Text type="secondary" style={{ fontSize: 12 }}>
306
- 管理本 GAdmin 与 Temporal Worker 的连接配置
307
- </Text>
308
- </div>
309
- </div>
310
-
311
- {!loading && !loadError && existing && (
312
- <Space>
313
- <Button
314
- aria-label="测试连通性"
315
- icon={<ApiOutlined />}
316
- loading={testing}
317
- onClick={() => testConnection(toFormValues(existing))}
318
- >
319
- 测试连通性
320
- </Button>
321
- <Button
322
- type="primary"
323
- aria-label="修改配置"
324
- icon={<EditOutlined />}
325
- onClick={openEditor}
326
- >
327
- 修改配置
328
- </Button>
329
- </Space>
330
- )}
290
+ aria-label="测试连通性"
291
+ icon={<ApiOutlined />}
292
+ loading={testing}
293
+ onClick={() => testConnection(toFormValues(existing))}
294
+ >
295
+ 测试连通性
296
+ </Button>
297
+ <Button
298
+ type="primary"
299
+ aria-label="修改配置"
300
+ icon={<EditOutlined />}
301
+ onClick={openEditor}
302
+ >
303
+ 修改配置
304
+ </Button>
305
+ </Space>
331
306
  </div>
332
- </Card>
307
+ )}
333
308
 
334
309
  <Card>
335
310
  {loading ? (
@@ -66,6 +66,7 @@ export interface Workflow {
66
66
  runs?: {
67
67
  pending: number;
68
68
  running: number;
69
+ waiting_retry: number;
69
70
  completed: number;
70
71
  failed: number;
71
72
  cancelled: number;
@@ -93,6 +94,7 @@ export interface WorkflowOverview {
93
94
  };
94
95
  running: number;
95
96
  pending: number;
97
+ waitingRetry: number;
96
98
  }
97
99
 
98
100
  export interface WorkflowNodeType {