@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
@@ -257,7 +257,7 @@ export function devShellPlugin(): Plugin {
257
257
  ? 'index.html'
258
258
  : 'dev-shell-entry.html';
259
259
  const rawHtml = fs.readFileSync(
260
- path.resolve(__dirname, '..', '..', entryFile),
260
+ path.resolve(import.meta.dirname, '..', '..', entryFile),
261
261
  'utf-8',
262
262
  );
263
263
  const html = await server.transformIndexHtml(url, rawHtml);
@@ -277,7 +277,7 @@ export function devShellPlugin(): Plugin {
277
277
  (req.headers.accept ?? '').includes('text/html');
278
278
  if (isHtmlNav) {
279
279
  const rawHtml = fs.readFileSync(
280
- path.resolve(__dirname, '..', '..', 'index.html'),
280
+ path.resolve(import.meta.dirname, '..', '..', 'index.html'),
281
281
  'utf-8',
282
282
  );
283
283
  const html = await server.transformIndexHtml(url, rawHtml);
@@ -1,3 +1,3 @@
1
1
  .highlight-changed-row {
2
2
  background-color: var(--ant-color-error-bg);
3
- }
3
+ }
@@ -18,8 +18,7 @@ import {
18
18
  message,
19
19
  type TableColumnsType,
20
20
  } from 'antd';
21
- import { PageHeader } from 'components';
22
- import { PAGE_WRAPPER_STYLE, SEARCH_CARD_PROPS } from 'constants/layout';
21
+ import { SEARCH_CARD_PROPS } from 'constants/layout';
23
22
  import { useCallback, useEffect, useState } from 'react';
24
23
  import { PeopleGroupModal } from './PeopleGroupModal';
25
24
  import {
@@ -153,11 +152,23 @@ export default function PeopleGroupsPage() {
153
152
  ];
154
153
 
155
154
  return (
156
- <div style={PAGE_WRAPPER_STYLE}>
157
- <PageHeader
158
- title="人员组"
159
- subtitle="将人员或已有人员组组合为可复用的审批对象。"
160
- extra={
155
+ <>
156
+ <Card {...SEARCH_CARD_PROPS}>
157
+ <div
158
+ style={{
159
+ display: 'flex',
160
+ justifyContent: 'space-between',
161
+ alignItems: 'center',
162
+ flexWrap: 'wrap',
163
+ gap: 8,
164
+ }}
165
+ >
166
+ <Input.Search
167
+ allowClear
168
+ placeholder="搜索名称或组标识"
169
+ style={{ width: 280 }}
170
+ onSearch={setQuery}
171
+ />
161
172
  <Space>
162
173
  <Button icon={<ReloadOutlined />} onClick={load} />
163
174
  <Button
@@ -168,15 +179,7 @@ export default function PeopleGroupsPage() {
168
179
  新建人员组
169
180
  </Button>
170
181
  </Space>
171
- }
172
- />
173
- <Card {...SEARCH_CARD_PROPS}>
174
- <Input.Search
175
- allowClear
176
- placeholder="搜索名称或组标识"
177
- style={{ width: 280 }}
178
- onSearch={setQuery}
179
- />
182
+ </div>
180
183
  </Card>
181
184
  <Card className="card-table">
182
185
  <Table
@@ -198,6 +201,6 @@ export default function PeopleGroupsPage() {
198
201
  }}
199
202
  />
200
203
  )}
201
- </div>
204
+ </>
202
205
  );
203
206
  }
@@ -18,8 +18,7 @@ import {
18
18
  message,
19
19
  type TableColumnsType,
20
20
  } from 'antd';
21
- import { PageHeader } from 'components';
22
- import { PAGE_WRAPPER_STYLE, SEARCH_CARD_PROPS } from 'constants/layout';
21
+ import { SEARCH_CARD_PROPS } from 'constants/layout';
23
22
  import { useCallback, useEffect, useState } from 'react';
24
23
  import { PersonModal } from './PersonModal';
25
24
  import {
@@ -140,11 +139,41 @@ export default function PersonDirectoryPage() {
140
139
  ];
141
140
 
142
141
  return (
143
- <div style={PAGE_WRAPPER_STYLE}>
144
- <PageHeader
145
- title="人员目录"
146
- subtitle="维护可被审批、人员组等功能引用的人员;未授予角色的人员不能访问系统。"
147
- extra={
142
+ <>
143
+ <Card {...SEARCH_CARD_PROPS}>
144
+ <div
145
+ style={{
146
+ display: 'flex',
147
+ justifyContent: 'space-between',
148
+ alignItems: 'center',
149
+ flexWrap: 'wrap',
150
+ gap: 8,
151
+ }}
152
+ >
153
+ <Space wrap>
154
+ <Input.Search
155
+ allowClear
156
+ placeholder="搜索姓名或账号"
157
+ style={{ width: 260 }}
158
+ onSearch={(value) => {
159
+ setPage(1);
160
+ setQuery(value);
161
+ }}
162
+ />
163
+ <Select
164
+ allowClear
165
+ placeholder="全部状态"
166
+ style={{ width: 140 }}
167
+ options={[
168
+ { label: '已启用', value: true },
169
+ { label: '已停用', value: false },
170
+ ]}
171
+ onChange={(value) => {
172
+ setPage(1);
173
+ setActive(value);
174
+ }}
175
+ />
176
+ </Space>
148
177
  <Space>
149
178
  <Button icon={<ReloadOutlined />} onClick={load} />
150
179
  <Button
@@ -155,33 +184,7 @@ export default function PersonDirectoryPage() {
155
184
  添加人员
156
185
  </Button>
157
186
  </Space>
158
- }
159
- />
160
- <Card {...SEARCH_CARD_PROPS}>
161
- <Space wrap>
162
- <Input.Search
163
- allowClear
164
- placeholder="搜索姓名或账号"
165
- style={{ width: 260 }}
166
- onSearch={(value) => {
167
- setPage(1);
168
- setQuery(value);
169
- }}
170
- />
171
- <Select
172
- allowClear
173
- placeholder="全部状态"
174
- style={{ width: 140 }}
175
- options={[
176
- { label: '已启用', value: true },
177
- { label: '已停用', value: false },
178
- ]}
179
- onChange={(value) => {
180
- setPage(1);
181
- setActive(value);
182
- }}
183
- />
184
- </Space>
187
+ </div>
185
188
  </Card>
186
189
  <Card className="card-table">
187
190
  <Table
@@ -209,6 +212,6 @@ export default function PersonDirectoryPage() {
209
212
  }}
210
213
  />
211
214
  )}
212
- </div>
215
+ </>
213
216
  );
214
217
  }
@@ -28,6 +28,7 @@ const PAGE_SIZE = 20;
28
28
  const STATUS_COLOR: Record<string, string> = {
29
29
  PENDING: 'gold',
30
30
  RUNNING: 'blue',
31
+ WAITING_RETRY: 'orange',
31
32
  COMPLETED: 'green',
32
33
  FAILED: 'red',
33
34
  CANCELLED: 'default',
@@ -222,7 +223,7 @@ export default function BusinessEventDetailPage() {
222
223
  <Typography.Text code>{event.registrationCode}</Typography.Text>
223
224
  ) : null
224
225
  }
225
- onBack={() => navigate('/admin/workflow/business-events')}
226
+ onBack={() => navigate('/admin/workflow/settings?tab=events')}
226
227
  backLabel={t('common.back')}
227
228
  actions={
228
229
  <Select
@@ -236,6 +237,10 @@ export default function BusinessEventDetailPage() {
236
237
  options={[
237
238
  { value: 'PENDING', label: t('instances.statuses.pending') },
238
239
  { value: 'RUNNING', label: t('instances.statuses.running') },
240
+ {
241
+ value: 'WAITING_RETRY',
242
+ label: t('instances.statuses.waiting_retry'),
243
+ },
239
244
  {
240
245
  value: 'COMPLETED',
241
246
  label: t('instances.statuses.completed'),
@@ -13,8 +13,7 @@ import {
13
13
  message,
14
14
  } from 'antd';
15
15
  import type { TableColumnsType } from 'antd';
16
- import { PageHeader } from 'components';
17
- import { PAGE_WRAPPER_STYLE, SEARCH_CARD_PROPS } from 'constants/layout';
16
+ import { SEARCH_CARD_PROPS } from 'constants/layout';
18
17
  import { useCallback, useEffect, useMemo, useState } from 'react';
19
18
  import { useTranslation } from 'react-i18next';
20
19
  import { useNavigate } from 'react-router-dom';
@@ -183,11 +182,29 @@ export default function BusinessEventCatalogPage() {
183
182
  ];
184
183
 
185
184
  return (
186
- <div className="workflow-page-stack" style={PAGE_WRAPPER_STYLE}>
187
- <PageHeader
188
- title={t('eventCatalog.title')}
189
- subtitle={t('eventCatalog.subtitle')}
190
- extra={
185
+ <div className="workflow-page-stack">
186
+ <Card {...SEARCH_CARD_PROPS}>
187
+ <div
188
+ style={{
189
+ display: 'flex',
190
+ justifyContent: 'space-between',
191
+ alignItems: 'center',
192
+ flexWrap: 'wrap',
193
+ gap: 8,
194
+ }}
195
+ >
196
+ <div className="workflow-event-catalog__filters">
197
+ <Input.Search
198
+ allowClear
199
+ value={query}
200
+ placeholder={t('eventCatalog.searchPlaceholder')}
201
+ onChange={(event) => setQuery(event.target.value)}
202
+ className="workflow-event-catalog__search"
203
+ />
204
+ <Typography.Text type="secondary" style={{ fontSize: 12 }}>
205
+ {t('eventCatalog.resultCount', { count: filteredEvents.length })}
206
+ </Typography.Text>
207
+ </div>
191
208
  <Space>
192
209
  <Button icon={<ReloadOutlined />} onClick={load} loading={loading}>
193
210
  {t('eventCatalog.refresh')}
@@ -200,21 +217,6 @@ export default function BusinessEventCatalogPage() {
200
217
  {t('eventCatalog.registration.register')}
201
218
  </Button>
202
219
  </Space>
203
- }
204
- />
205
-
206
- <Card {...SEARCH_CARD_PROPS}>
207
- <div className="workflow-event-catalog__filters">
208
- <Input.Search
209
- allowClear
210
- value={query}
211
- placeholder={t('eventCatalog.searchPlaceholder')}
212
- onChange={(event) => setQuery(event.target.value)}
213
- className="workflow-event-catalog__search"
214
- />
215
- <Typography.Text type="secondary" style={{ fontSize: 12 }}>
216
- {t('eventCatalog.resultCount', { count: filteredEvents.length })}
217
- </Typography.Text>
218
220
  </div>
219
221
  </Card>
220
222
 
@@ -76,7 +76,6 @@ interface EnhancedFlowRendererProps {
76
76
  onDslChange?: (dsl: WorkflowDSL) => void;
77
77
  // New props for execution status
78
78
  nodeExecutions?: NodeExecution[];
79
- reusedNodeIds?: string[];
80
79
  showExecutionStatus?: boolean;
81
80
  }
82
81
 
@@ -103,7 +102,6 @@ function EnhancedFlowCanvas({
103
102
  onNodeClick,
104
103
  onDslChange,
105
104
  nodeExecutions,
106
- reusedNodeIds,
107
105
  showExecutionStatus,
108
106
  }: EnhancedFlowRendererProps) {
109
107
  const { t } = useTranslation('workflow');
@@ -134,11 +132,6 @@ function EnhancedFlowCanvas({
134
132
  });
135
133
  return map;
136
134
  }, [nodeExecutions]);
137
- const reusedNodeSet = useMemo(
138
- () => new Set(reusedNodeIds ?? []),
139
- [reusedNodeIds],
140
- );
141
-
142
135
  const { nodes, edges } = useMemo(() => {
143
136
  if (!dsl) return { nodes: [] as Node[], edges: [] as Edge[] };
144
137
 
@@ -178,9 +171,6 @@ function EnhancedFlowCanvas({
178
171
  execution.finishedAt,
179
172
  );
180
173
  nodeData.error = execution.error;
181
- } else if (reusedNodeSet.has(n.id)) {
182
- nodeData.executionStatus = 'COMPLETED';
183
- nodeData.executionStatusLabel = t('instanceDetail.reusedResult');
184
174
  } else {
185
175
  nodeData.executionStatus = 'NOT_EXECUTED';
186
176
  }
@@ -234,7 +224,6 @@ function EnhancedFlowCanvas({
234
224
  selectedNodeId,
235
225
  selectedEdgeId,
236
226
  executionMap,
237
- reusedNodeSet,
238
227
  showExecutionStatus,
239
228
  readonly,
240
229
  deleteEdges,
@@ -198,6 +198,30 @@ export function StatusCards({ overview, activeFilter, onFilter }: Props) {
198
198
  </div>
199
199
  </Card>
200
200
  </Col>
201
+
202
+ {/* Waiting for retry — clickable */}
203
+ <Col flex="1">
204
+ <Card
205
+ size="small"
206
+ hoverable
207
+ onClick={() => handleClick('WAITING_RETRY')}
208
+ style={cardStyle('WAITING_RETRY')}
209
+ >
210
+ <Text type="secondary" style={{ fontSize: 14 }}>
211
+ {t('status.waitingRetry')}
212
+ </Text>
213
+ <div
214
+ style={{
215
+ fontSize: 24,
216
+ fontWeight: 600,
217
+ lineHeight: 1.3,
218
+ color: '#fa8c16',
219
+ }}
220
+ >
221
+ {overview.waitingRetry ?? 0}
222
+ </div>
223
+ </Card>
224
+ </Col>
201
225
  </Row>
202
226
  );
203
227
  }
@@ -19,8 +19,7 @@ import {
19
19
  PlusOutlined,
20
20
  ReloadOutlined,
21
21
  } from '@ant-design/icons';
22
- import { PageHeader } from 'components';
23
- import { PAGE_WRAPPER_STYLE, SEARCH_CARD_PROPS } from 'constants/layout';
22
+ import { SEARCH_CARD_PROPS } from 'constants/layout';
24
23
  import { ConnectionModal } from './ConnectionModal';
25
24
  import type { IntegrationConnection } from './connection.types';
26
25
  import { deleteConnection, fetchConnections } from './connectionsApi';
@@ -186,11 +185,24 @@ export default function ConnectionsPage() {
186
185
  ];
187
186
 
188
187
  return (
189
- <div className="workflow-page-stack" style={PAGE_WRAPPER_STYLE}>
190
- <PageHeader
191
- title={t('connection.title')}
192
- subtitle={t('connection.subtitle')}
193
- extra={
188
+ <div className="workflow-page-stack">
189
+ {/* Search Card */}
190
+ <Card {...SEARCH_CARD_PROPS}>
191
+ <div
192
+ style={{
193
+ display: 'flex',
194
+ justifyContent: 'space-between',
195
+ alignItems: 'center',
196
+ flexWrap: 'wrap',
197
+ gap: 8,
198
+ }}
199
+ >
200
+ <Input.Search
201
+ allowClear
202
+ placeholder={t('connection.search')}
203
+ onSearch={setQuery}
204
+ style={{ width: 300 }}
205
+ />
194
206
  <Space>
195
207
  <Button icon={<ReloadOutlined />} onClick={load}>
196
208
  {t('connection.refresh')}
@@ -203,17 +215,7 @@ export default function ConnectionsPage() {
203
215
  {t('connection.new')}
204
216
  </Button>
205
217
  </Space>
206
- }
207
- />
208
-
209
- {/* Search Card */}
210
- <Card {...SEARCH_CARD_PROPS}>
211
- <Input.Search
212
- allowClear
213
- placeholder={t('connection.search')}
214
- onSearch={setQuery}
215
- style={{ width: 300 }}
216
- />
218
+ </div>
217
219
  </Card>
218
220
 
219
221
  {/* Content Card */}
@@ -288,6 +288,7 @@ export default function WorkflowListPage() {
288
288
  const runs = record.runs || {
289
289
  pending: 0,
290
290
  running: 0,
291
+ waiting_retry: 0,
291
292
  completed: 0,
292
293
  failed: 0,
293
294
  cancelled: 0,
@@ -316,6 +317,12 @@ export default function WorkflowListPage() {
316
317
  label={t('list.running')}
317
318
  onClick={() => go('RUNNING')}
318
319
  />
320
+ <RunDot
321
+ count={runs.waiting_retry ?? 0}
322
+ color="#fa8c16"
323
+ label={t('list.waitingRetry')}
324
+ onClick={() => go('WAITING_RETRY')}
325
+ />
319
326
  <RunDot
320
327
  count={runs.failed}
321
328
  color="#ff4d4f"
@@ -40,6 +40,11 @@ const STATUS_TAG: Record<
40
40
  > = {
41
41
  PENDING: { color: 'gold', label: 'Pending', icon: <ClockCircleOutlined /> },
42
42
  RUNNING: { color: 'blue', label: 'Running', icon: <LoadingOutlined /> },
43
+ WAITING_RETRY: {
44
+ color: 'orange',
45
+ label: 'Waiting to retry',
46
+ icon: <ClockCircleOutlined />,
47
+ },
43
48
  COMPLETED: {
44
49
  color: 'green',
45
50
  label: 'Completed',
@@ -65,7 +70,6 @@ interface NodeExecution {
65
70
  interface RetryAttempt {
66
71
  id: string;
67
72
  status: string;
68
- retryFromNodeId: string | null;
69
73
  createdAt: string;
70
74
  }
71
75
 
@@ -88,9 +92,7 @@ interface InstanceDetail {
88
92
  approvableNodeIds: string[];
89
93
  retryableNodeIds: string[];
90
94
  retryAttempts: RetryAttempt[];
91
- reusedNodeIds: string[];
92
95
  retryOfInstanceId: string | null;
93
- retryFromNodeId: string | null;
94
96
  dsl?: WorkflowDSL;
95
97
  }
96
98
 
@@ -221,7 +223,11 @@ export default function WorkflowInstanceDetailPage() {
221
223
 
222
224
  // Auto-refresh for running instances
223
225
  useEffect(() => {
224
- if (data?.status === 'RUNNING' || data?.status === 'PENDING') {
226
+ if (
227
+ data?.status === 'RUNNING' ||
228
+ data?.status === 'PENDING' ||
229
+ data?.status === 'WAITING_RETRY'
230
+ ) {
225
231
  const interval = setInterval(fetchData, 3000);
226
232
  return () => clearInterval(interval);
227
233
  }
@@ -263,16 +269,16 @@ export default function WorkflowInstanceDetailPage() {
263
269
  }
264
270
  };
265
271
 
266
- const handleRetryFromNode = async (nodeId: string) => {
272
+ const handleRetryFailedNode = async (nodeId: string) => {
267
273
  setActionLoading(true);
268
274
  try {
269
- const res = await customRequest<{ instanceId: string }>(
270
- `workflow/instance/${instanceId}/retry-from-node`,
275
+ await customRequest(
276
+ `workflow/instance/${instanceId}/retry-failed-node`,
271
277
  'POST',
272
278
  { nodeId },
273
279
  );
274
280
  message.success(t('instanceDetail.retryFromNodeStarted'));
275
- navigate(`/admin/workflow/instance/show/${res.instanceId}`);
281
+ await fetchData();
276
282
  } catch (e: any) {
277
283
  message.error(e?.message || t('instanceDetail.retryFromNodeFailed'));
278
284
  } finally {
@@ -325,17 +331,10 @@ export default function WorkflowInstanceDetailPage() {
325
331
  selectedNodeId && data.dsl
326
332
  ? data.dsl.nodes.find((n) => n.id === selectedNodeId)
327
333
  : null;
328
- const selectedNodeWasReused = selectedNodeId
329
- ? data.reusedNodeIds.includes(selectedNodeId)
330
- : false;
331
334
  const latestRetryAttempt = data.retryAttempts[0];
332
- const selectedRetryAttempt = selectedNodeId
333
- ? data.retryAttempts.find(
334
- (attempt) => attempt.retryFromNodeId === selectedNodeId,
335
- )
336
- : undefined;
337
- const workflowFailure =
338
- data.status === 'FAILED' ? getWorkflowFailureSummary(data.result) : null;
335
+ const workflowFailure = ['FAILED', 'WAITING_RETRY'].includes(data.status)
336
+ ? getWorkflowFailureSummary(data.result)
337
+ : null;
339
338
 
340
339
  return (
341
340
  <div className="workflow-page-stack">
@@ -383,7 +382,7 @@ export default function WorkflowInstanceDetailPage() {
383
382
  {t('instanceDetail.openTemporal')}
384
383
  </Button>
385
384
  </Tooltip>
386
- {(data.status === 'RUNNING' || data.status === 'PENDING') && (
385
+ {['RUNNING', 'PENDING', 'WAITING_RETRY'].includes(data.status) && (
387
386
  <Popconfirm
388
387
  title="Cancel this workflow execution?"
389
388
  onConfirm={handleCancel}
@@ -480,17 +479,10 @@ export default function WorkflowInstanceDetailPage() {
480
479
  </Button>
481
480
  </Descriptions.Item>
482
481
  )}
483
- {data.reusedNodeIds.length > 0 && (
484
- <Descriptions.Item label={t('instanceDetail.reusedResults')}>
485
- {t('instanceDetail.reusedResultsCount', {
486
- count: data.reusedNodeIds.length,
487
- })}
488
- </Descriptions.Item>
489
- )}
490
482
  </Descriptions>
491
483
  {workflowFailure && (
492
484
  <Alert
493
- type="error"
485
+ type={data.status === 'WAITING_RETRY' ? 'warning' : 'error'}
494
486
  showIcon
495
487
  style={{ marginTop: 16 }}
496
488
  message={workflowFailure.message}
@@ -571,9 +563,6 @@ export default function WorkflowInstanceDetailPage() {
571
563
  const canRetryFromNode =
572
564
  ne.status === 'FAILED' &&
573
565
  data.retryableNodeIds.includes(ne.nodeId);
574
- const retryAttempt = data.retryAttempts.find(
575
- (attempt) => attempt.retryFromNodeId === ne.nodeId,
576
- );
577
566
  return {
578
567
  title: (
579
568
  <Space
@@ -620,7 +609,7 @@ export default function WorkflowInstanceDetailPage() {
620
609
  description={t(
621
610
  'instanceDetail.retryFromNodeDescription',
622
611
  )}
623
- onConfirm={() => handleRetryFromNode(ne.nodeId)}
612
+ onConfirm={() => handleRetryFailedNode(ne.nodeId)}
624
613
  okText={t('instanceDetail.retryFromNode')}
625
614
  cancelText={t('common.cancel')}
626
615
  >
@@ -635,23 +624,6 @@ export default function WorkflowInstanceDetailPage() {
635
624
  </Button>
636
625
  </Popconfirm>
637
626
  )}
638
- {retryAttempt && (
639
- <Button
640
- size="small"
641
- type="link"
642
- icon={<LinkOutlined />}
643
- onClick={(event) => {
644
- event.stopPropagation();
645
- navigate(
646
- `/admin/workflow/instance/show/${retryAttempt.id}`,
647
- );
648
- }}
649
- >
650
- {t('instanceDetail.viewRetryAttempt', {
651
- id: retryAttempt.id,
652
- })}
653
- </Button>
654
- )}
655
627
  </Space>
656
628
  ),
657
629
  description: (
@@ -821,7 +793,6 @@ export default function WorkflowInstanceDetailPage() {
821
793
  showExecutionStatus={true}
822
794
  selectedNodeId={selectedNodeId}
823
795
  readonly={true}
824
- reusedNodeIds={data.reusedNodeIds}
825
796
  onNodeClick={(nodeId) => setSelectedNodeId(nodeId)}
826
797
  />
827
798
  </div>
@@ -982,7 +953,7 @@ export default function WorkflowInstanceDetailPage() {
982
953
  'instanceDetail.retryFromNodeDescription',
983
954
  )}
984
955
  onConfirm={() =>
985
- handleRetryFromNode(selectedExecution.nodeId)
956
+ handleRetryFailedNode(selectedExecution.nodeId)
986
957
  }
987
958
  okText={t('instanceDetail.retryFromNode')}
988
959
  cancelText={t('common.cancel')}
@@ -997,22 +968,6 @@ export default function WorkflowInstanceDetailPage() {
997
968
  </Popconfirm>
998
969
  )}
999
970
 
1000
- {selectedExecution.status === 'FAILED' &&
1001
- selectedRetryAttempt && (
1002
- <Button
1003
- icon={<LinkOutlined />}
1004
- onClick={() =>
1005
- navigate(
1006
- `/admin/workflow/instance/show/${selectedRetryAttempt.id}`,
1007
- )
1008
- }
1009
- >
1010
- {t('instanceDetail.viewRetryAttempt', {
1011
- id: selectedRetryAttempt.id,
1012
- })}
1013
- </Button>
1014
- )}
1015
-
1016
971
  {/* Approval actions */}
1017
972
  {selectedExecution.nodeType === 'approval' &&
1018
973
  selectedExecution.status === 'RUNNING' &&
@@ -1047,14 +1002,10 @@ export default function WorkflowInstanceDetailPage() {
1047
1002
  <div>
1048
1003
  <div style={{ marginBottom: 16 }}>
1049
1004
  <Title level={5}>{selectedDslNode.label}</Title>
1050
- <Tag
1051
- color={selectedNodeWasReused ? 'green' : 'default'}
1052
- >
1053
- {selectedNodeWasReused
1054
- ? t('instanceDetail.reusedResult')
1055
- : instanceHasFinished
1056
- ? 'Not Executed / Skipped'
1057
- : 'Not Executed'}
1005
+ <Tag>
1006
+ {instanceHasFinished
1007
+ ? 'Not Executed / Skipped'
1008
+ : 'Not Executed'}
1058
1009
  </Tag>
1059
1010
  </div>
1060
1011
  <Descriptions column={1} size="small">
@@ -1063,11 +1014,9 @@ export default function WorkflowInstanceDetailPage() {
1063
1014
  </Descriptions.Item>
1064
1015
  <Descriptions.Item label="Status">
1065
1016
  <Text type="secondary">
1066
- {selectedNodeWasReused
1067
- ? t('instanceDetail.reusedResultDescription')
1068
- : instanceHasFinished
1069
- ? 'Not executed in this run'
1070
- : 'Waiting for execution'}
1017
+ {instanceHasFinished
1018
+ ? 'Not executed in this run'
1019
+ : 'Waiting for execution'}
1071
1020
  </Text>
1072
1021
  </Descriptions.Item>
1073
1022
  </Descriptions>