@gadmin2n/schematics 0.0.147 → 0.0.149

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 (106) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +2 -1
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permission.seed.ts +67 -15
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +83 -53
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/index.ts +5 -0
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.spec.ts +24 -0
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.ts +15 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +4 -0
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.service.ts +31 -4
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-runtime.controller.spec.ts +2 -6
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.spec.ts +28 -29
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.ts +16 -34
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.dto.ts +3 -3
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.service.spec.ts +61 -6
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.controller.spec.ts +2 -2
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +2 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.controller.ts +11 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.service.spec.ts +126 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.service.ts +136 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.controller.ts +24 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.dto.ts +8 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.service.spec.ts +62 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.service.ts +94 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.types.ts +6 -0
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staffDirectory.module.ts +10 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user.provider.ts +6 -2
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.spec.ts +20 -12
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.ts +21 -19
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +11 -0
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +97 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.ts +150 -2
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.controller.ts +13 -1
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.dto.ts +13 -1
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.errors.ts +1 -0
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.service.spec.ts +125 -0
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.service.ts +118 -5
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-cancellation.spec.ts +20 -3
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +6 -9
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-catalog.spec.ts +166 -17
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-trigger.pg.spec.ts +82 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-runtime.facade.ts +2 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.spec.ts +4 -1
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +5 -1
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.dto.ts +7 -2
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +44 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +106 -37
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.spec.ts +59 -3
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.spec.ts +33 -22
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.ts +10 -23
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/StaffSelect.test.tsx +69 -0
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/StaffSelect.tsx +145 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/index.ts +2 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/staff-directory.api.ts +16 -0
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +1 -0
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.test.ts +10 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +20 -2
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +6 -0
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +60 -14
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +60 -14
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +41 -1
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/PeopleGroupModal.tsx +181 -0
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/index.tsx +203 -0
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/people-groups.api.ts +145 -0
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/PersonModal.tsx +112 -0
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/index.tsx +214 -0
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/person-directory.api.ts +79 -0
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/form-modal.tsx +7 -11
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +7 -7
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/RegisterBusinessEventModal.test.tsx +46 -5
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/RegisterBusinessEventModal.tsx +14 -10
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.test.tsx +1 -1
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.tsx +2 -1
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.test.tsx +1 -1
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.tsx +2 -2
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeOutputSummary.test.tsx +20 -0
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.test.tsx +97 -28
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +87 -119
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/WorkflowCodeEditor.test.tsx +22 -1
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/WorkflowCodeEditor.tsx +8 -2
  79. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/connections.css +8 -46
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/connectionsApi.ts +7 -0
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.test.tsx +32 -3
  82. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.tsx +163 -106
  83. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +8 -1
  84. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.test.tsx +26 -0
  85. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +34 -6
  86. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-config.css +13 -16
  87. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-instance-show.css +185 -0
  88. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +224 -105
  89. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/CodeConfigEditor.test.tsx +1 -1
  90. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/DbExecuteConfigEditor.tsx +21 -12
  91. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.test.tsx +140 -0
  92. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.tsx +124 -62
  93. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.test.tsx +160 -8
  94. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.tsx +125 -55
  95. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SimplifiedNodeEditors.test.tsx +78 -17
  96. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubjectPicker.test.tsx +33 -1
  97. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubjectPicker.tsx +28 -46
  98. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/event-catalog.api.ts +4 -2
  99. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/human-approval-config.css +16 -0
  100. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/subject-provider.api.ts +0 -6
  101. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +24 -0
  102. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +14 -17
  103. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +1 -0
  104. package/package.json +1 -1
  105. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CreateNodeInstanceDrawer.tsx +0 -162
  106. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/UserGroupCreateModal.tsx +0 -72
@@ -0,0 +1,140 @@
1
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
2
+ import { useState } from 'react';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import i18n from '../../../../i18n';
5
+ import { EventTriggerConfigEditor } from './EventTriggerConfigEditor';
6
+
7
+ vi.mock('./event-catalog.api', () => ({
8
+ eventCatalogApi: {
9
+ list: vi.fn().mockResolvedValue([
10
+ {
11
+ registrationCode: 'request',
12
+ label: '申请变化',
13
+ schemaName: 'public',
14
+ tableName: 'request',
15
+ operations: ['INSERT', 'UPDATE'],
16
+ watchedFields: ['status'],
17
+ primaryKeyFields: ['id'],
18
+ fields: [
19
+ {
20
+ path: 'primaryKey.id',
21
+ type: 'number',
22
+ primaryKey: true,
23
+ nullable: false,
24
+ },
25
+ {
26
+ path: 'before.status',
27
+ type: 'string',
28
+ primaryKey: false,
29
+ nullable: true,
30
+ },
31
+ {
32
+ path: 'after.status',
33
+ type: 'string',
34
+ primaryKey: false,
35
+ nullable: false,
36
+ },
37
+ ],
38
+ },
39
+ ]),
40
+ },
41
+ }));
42
+
43
+ Object.defineProperty(window, 'matchMedia', {
44
+ writable: true,
45
+ value: () => ({
46
+ matches: false,
47
+ addListener() {},
48
+ removeListener() {},
49
+ addEventListener() {},
50
+ removeEventListener() {},
51
+ dispatchEvent: () => false,
52
+ }),
53
+ });
54
+
55
+ function Editor() {
56
+ const [value, setValue] = useState<
57
+ NonNullable<Parameters<typeof EventTriggerConfigEditor>[0]['value']>
58
+ >({
59
+ registrationCode: 'request',
60
+ processingMode: 'PARALLEL',
61
+ operationRules: [{ operation: 'INSERT' }, { operation: 'UPDATE' }],
62
+ });
63
+ return (
64
+ <>
65
+ <EventTriggerConfigEditor
66
+ value={value}
67
+ onChange={(next) => setValue(next!)}
68
+ schema={{ type: 'object' }}
69
+ />
70
+ <output data-testid="config">{JSON.stringify(value)}</output>
71
+ </>
72
+ );
73
+ }
74
+
75
+ describe('event operation condition editor', () => {
76
+ it('preserves separate operation conditions, supports value lists and disabling one operation', async () => {
77
+ await i18n.changeLanguage('zh_CN');
78
+ render(<Editor />);
79
+ await screen.findByText('新增时');
80
+ fireEvent.click(screen.getByText('满足条件时启动'));
81
+ fireEvent.change(screen.getByRole('textbox', { name: '条件 1 的比较值' }), {
82
+ target: { value: 'PENDING' },
83
+ });
84
+ fireEvent.click(screen.getByText('修改时'));
85
+ fireEvent.click(screen.getByText('满足条件时启动'));
86
+ fireEvent.mouseDown(
87
+ screen.getByRole('combobox', { name: '条件 1 的判断方式' }),
88
+ );
89
+ const operators = await screen.findAllByText('属于以下任一值');
90
+ fireEvent.click(operators[operators.length - 1]);
91
+ const input = screen.getByRole('combobox', { name: '条件 1 的比较值' });
92
+ for (const value of ['DRAFT', 'REJECTED']) {
93
+ fireEvent.change(input, { target: { value } });
94
+ fireEvent.keyDown(input, { key: 'Enter', code: 'Enter', keyCode: 13 });
95
+ fireEvent.keyUp(input, { key: 'Enter', code: 'Enter', keyCode: 13 });
96
+ await waitFor(() =>
97
+ expect(screen.getByTestId('config').textContent).toContain(value),
98
+ );
99
+ }
100
+ let config = JSON.parse(screen.getByTestId('config').textContent!);
101
+ expect(config.operationRules).toEqual(
102
+ expect.arrayContaining([
103
+ expect.objectContaining({
104
+ operation: 'INSERT',
105
+ match: expect.objectContaining({
106
+ conditions: [
107
+ expect.objectContaining({
108
+ left: { source: 'WORKFLOW', path: 'after.status' },
109
+ right: expect.objectContaining({ value: 'PENDING' }),
110
+ }),
111
+ ],
112
+ }),
113
+ }),
114
+ expect.objectContaining({
115
+ operation: 'UPDATE',
116
+ match: expect.objectContaining({
117
+ conditions: [
118
+ expect.objectContaining({
119
+ operator: 'IN',
120
+ left: { source: 'WORKFLOW', path: 'before.status' },
121
+ right: expect.objectContaining({
122
+ value: '["DRAFT","REJECTED"]',
123
+ }),
124
+ }),
125
+ ],
126
+ }),
127
+ }),
128
+ ]),
129
+ );
130
+ fireEvent.click(screen.getByText('新增时'));
131
+ expect(screen.getByDisplayValue('PENDING')).toBeInTheDocument();
132
+ fireEvent.click(screen.getByText('不启动'));
133
+ config = JSON.parse(screen.getByTestId('config').textContent!);
134
+ expect(
135
+ config.operationRules.map(
136
+ (rule: { operation: string }) => rule.operation,
137
+ ),
138
+ ).toEqual(['UPDATE']);
139
+ });
140
+ });
@@ -14,17 +14,24 @@ import {
14
14
  import { useEffect, useMemo, useState } from 'react';
15
15
  import { useTranslation } from 'react-i18next';
16
16
  import type { WidgetProps } from '../schema-form/widget-registry';
17
- import { eventCatalogApi, type BusinessEventOption } from './event-catalog.api';
17
+ import {
18
+ eventCatalogApi,
19
+ type BusinessEventOption,
20
+ type BusinessEventOperation,
21
+ } from './event-catalog.api';
18
22
 
19
23
  type EventTriggerConfig = {
20
24
  registrationCode: string;
21
- match?: EventMatch;
25
+ operationRules: Array<{
26
+ operation: BusinessEventOperation;
27
+ match?: EventMatch;
28
+ }>;
22
29
  processingMode: 'PARALLEL' | 'SERIAL_BY_KEY';
23
30
  concurrencyKeyPath?: string;
24
31
  maxConcurrency?: number | null;
25
32
  };
26
33
 
27
- type EventMatchOperator = 'EQ' | 'NEQ' | 'EMPTY' | 'NOT_EMPTY';
34
+ type EventMatchOperator = 'EQ' | 'NEQ' | 'IN' | 'EMPTY' | 'NOT_EMPTY';
28
35
  type EventMatchCondition = {
29
36
  id: string;
30
37
  left: { source: 'WORKFLOW'; path: string };
@@ -42,6 +49,7 @@ type EventMatch = {
42
49
 
43
50
  const DEFAULT_CONFIG: EventTriggerConfig = {
44
51
  registrationCode: '',
52
+ operationRules: [],
45
53
  processingMode: 'PARALLEL',
46
54
  };
47
55
 
@@ -81,6 +89,8 @@ export function EventTriggerConfigEditor({
81
89
  const [loading, setLoading] = useState(true);
82
90
  const [loadFailed, setLoadFailed] = useState(false);
83
91
  const config = { ...DEFAULT_CONFIG, ...value };
92
+ const [activeOperation, setActiveOperation] =
93
+ useState<BusinessEventOperation>('INSERT');
84
94
  const selectedEvent = useMemo(
85
95
  () =>
86
96
  events.find(
@@ -88,6 +98,24 @@ export function EventTriggerConfigEditor({
88
98
  ),
89
99
  [config.registrationCode, events],
90
100
  );
101
+ const operation = selectedEvent?.operations.includes(activeOperation)
102
+ ? activeOperation
103
+ : selectedEvent?.operations[0];
104
+ const rule = config.operationRules.find(
105
+ (item) => item.operation === operation,
106
+ );
107
+ const match = rule?.match;
108
+
109
+ function changeMatch(next?: EventMatch, enabled = true) {
110
+ if (!operation) return;
111
+ onChange({
112
+ ...config,
113
+ operationRules: [
114
+ ...config.operationRules.filter((item) => item.operation !== operation),
115
+ ...(enabled ? [{ operation, ...(next ? { match: next } : {}) }] : []),
116
+ ],
117
+ });
118
+ }
91
119
  const serialKeyFields = useMemo(
92
120
  () =>
93
121
  (selectedEvent?.fields ?? []).filter(
@@ -99,10 +127,13 @@ export function EventTriggerConfigEditor({
99
127
  );
100
128
  const matchFields = useMemo(
101
129
  () =>
102
- (selectedEvent?.fields ?? []).filter((field) =>
103
- ['string', 'number', 'boolean'].includes(field.type),
130
+ (selectedEvent?.fields ?? []).filter(
131
+ (field) =>
132
+ ['string', 'number', 'boolean'].includes(field.type) &&
133
+ !(operation === 'INSERT' && field.path.startsWith('before.')) &&
134
+ !(operation === 'DELETE' && field.path.startsWith('after.')),
104
135
  ),
105
- [selectedEvent],
136
+ [selectedEvent, operation],
106
137
  );
107
138
  const defaultMatchField =
108
139
  matchFields.find((field) => !field.primaryKey) ?? matchFields[0];
@@ -145,6 +176,9 @@ export function EventTriggerConfigEditor({
145
176
 
146
177
  onChange({
147
178
  registrationCode,
179
+ operationRules: (event?.operations ?? []).map((operation) => ({
180
+ operation,
181
+ })),
148
182
  processingMode: config.processingMode,
149
183
  ...(config.processingMode === 'SERIAL_BY_KEY'
150
184
  ? {
@@ -161,7 +195,7 @@ export function EventTriggerConfigEditor({
161
195
  if (processingMode === 'PARALLEL') {
162
196
  onChange({
163
197
  registrationCode: config.registrationCode,
164
- ...(config.match ? { match: config.match } : {}),
198
+ operationRules: config.operationRules,
165
199
  processingMode,
166
200
  });
167
201
  return;
@@ -180,31 +214,24 @@ export function EventTriggerConfigEditor({
180
214
  });
181
215
  }
182
216
 
183
- function changeMatchMode(mode: 'ALWAYS' | 'CONDITIONAL') {
184
- if (mode === 'ALWAYS') {
185
- const { match: _match, ...next } = config;
186
- onChange(next);
217
+ function changeMatchMode(mode: 'NEVER' | 'ALWAYS' | 'CONDITIONAL') {
218
+ if (mode !== 'CONDITIONAL') {
219
+ changeMatch(undefined, mode === 'ALWAYS');
187
220
  return;
188
221
  }
189
- onChange({
190
- ...config,
191
- match: {
192
- combineOperation: 'AND',
193
- conditions: [newMatchCondition(defaultMatchField)],
194
- },
222
+ changeMatch({
223
+ combineOperation: 'AND',
224
+ conditions: [newMatchCondition(defaultMatchField)],
195
225
  });
196
226
  }
197
227
 
198
228
  function updateMatchCondition(index: number, next: EventMatchCondition) {
199
- if (!config.match) return;
200
- onChange({
201
- ...config,
202
- match: {
203
- ...config.match,
204
- conditions: config.match.conditions.map((condition, current) =>
205
- current === index ? next : condition,
206
- ),
207
- },
229
+ if (!match) return;
230
+ changeMatch({
231
+ ...match,
232
+ conditions: match.conditions.map((condition, current) =>
233
+ current === index ? next : condition,
234
+ ),
208
235
  });
209
236
  }
210
237
 
@@ -268,8 +295,7 @@ export function EventTriggerConfigEditor({
268
295
  className="workflow-node-config__inline-message"
269
296
  >
270
297
  <code>
271
- {selectedEvent.schemaName}.{selectedEvent.tableName} ·{' '}
272
- {selectedEvent.watchedFields.join(', ')}
298
+ {selectedEvent.schemaName}.{selectedEvent.tableName}
273
299
  </code>
274
300
  </Typography.Text>
275
301
  )}
@@ -277,11 +303,23 @@ export function EventTriggerConfigEditor({
277
303
 
278
304
  <Card size="small" title={t('nodeEditors.event.startCondition')}>
279
305
  <Radio.Group
280
- value={config.match ? 'CONDITIONAL' : 'ALWAYS'}
306
+ value={operation}
307
+ onChange={(event) => setActiveOperation(event.target.value)}
308
+ style={{ display: 'block', marginBottom: 16 }}
309
+ >
310
+ {(selectedEvent?.operations ?? []).map((item) => (
311
+ <Radio.Button key={item} value={item}>
312
+ {t(`nodeEditors.event.operations.${item}`)}
313
+ </Radio.Button>
314
+ ))}
315
+ </Radio.Group>
316
+ <Radio.Group
317
+ value={!rule ? 'NEVER' : match ? 'CONDITIONAL' : 'ALWAYS'}
281
318
  disabled={disabled || !selectedEvent}
282
319
  onChange={(event) => changeMatchMode(event.target.value)}
283
320
  >
284
321
  <Space direction="vertical" size={8}>
322
+ <Radio value="NEVER">{t('nodeEditors.event.neverStart')}</Radio>
285
323
  <Radio value="ALWAYS">{t('nodeEditors.event.alwaysStart')}</Radio>
286
324
  <Radio value="CONDITIONAL">
287
325
  {t('nodeEditors.event.conditionalStart')}
@@ -289,23 +327,20 @@ export function EventTriggerConfigEditor({
289
327
  </Space>
290
328
  </Radio.Group>
291
329
 
292
- {config.match && (
330
+ {match && (
293
331
  <Space
294
332
  direction="vertical"
295
333
  size={10}
296
334
  style={{ width: '100%', marginTop: 14 }}
297
335
  >
298
- {config.match.conditions.length > 1 && (
336
+ {match.conditions.length > 1 && (
299
337
  <Radio.Group
300
- value={config.match.combineOperation}
338
+ value={match.combineOperation}
301
339
  disabled={disabled}
302
340
  onChange={(event) =>
303
- onChange({
304
- ...config,
305
- match: {
306
- ...config.match!,
307
- combineOperation: event.target.value,
308
- },
341
+ changeMatch({
342
+ ...match!,
343
+ combineOperation: event.target.value,
309
344
  })
310
345
  }
311
346
  >
@@ -318,7 +353,7 @@ export function EventTriggerConfigEditor({
318
353
  </Radio.Group>
319
354
  )}
320
355
 
321
- {config.match.conditions.map((condition, index) => {
356
+ {match.conditions.map((condition, index) => {
322
357
  const selectedField = matchFields.find(
323
358
  (field) => field.path === condition.left.path,
324
359
  );
@@ -356,7 +391,8 @@ export function EventTriggerConfigEditor({
356
391
  : {
357
392
  source: 'FIXED',
358
393
  valueType: fixedValueType(field),
359
- value: '',
394
+ value:
395
+ condition.operator === 'IN' ? '[]' : '',
360
396
  },
361
397
  });
362
398
  }}
@@ -369,7 +405,7 @@ export function EventTriggerConfigEditor({
369
405
  value={condition.operator}
370
406
  disabled={disabled}
371
407
  options={(
372
- ['EQ', 'NEQ', 'EMPTY', 'NOT_EMPTY'] as const
408
+ ['EQ', 'NEQ', 'IN', 'EMPTY', 'NOT_EMPTY'] as const
373
409
  ).map((operator) => ({
374
410
  value: operator,
375
411
  label: t(
@@ -382,17 +418,38 @@ export function EventTriggerConfigEditor({
382
418
  operator,
383
419
  right: UNARY_MATCH_OPERATORS.has(operator)
384
420
  ? undefined
385
- : (condition.right ?? {
421
+ : {
386
422
  source: 'FIXED',
387
423
  valueType: fixedValueType(selectedField),
388
- value: '',
389
- }),
424
+ value: operator === 'IN' ? '[]' : '',
425
+ },
390
426
  })
391
427
  }
392
428
  style={{ width: '30%' }}
393
429
  />
394
430
  {!UNARY_MATCH_OPERATORS.has(condition.operator) &&
395
- (selectedField?.type === 'boolean' ? (
431
+ (condition.operator === 'IN' ? (
432
+ <Select
433
+ mode="tags"
434
+ aria-label={t('nodeEditors.event.conditionValue', {
435
+ number: index + 1,
436
+ })}
437
+ value={parseValueList(condition.right?.value)}
438
+ disabled={disabled}
439
+ placeholder={t('nodeEditors.event.valueListHint')}
440
+ onChange={(values) =>
441
+ updateMatchCondition(index, {
442
+ ...condition,
443
+ right: {
444
+ source: 'FIXED',
445
+ valueType: fixedValueType(selectedField),
446
+ value: JSON.stringify(values),
447
+ },
448
+ })
449
+ }
450
+ style={{ width: '25%' }}
451
+ />
452
+ ) : selectedField?.type === 'boolean' ? (
396
453
  <Select
397
454
  aria-label={t('nodeEditors.event.conditionValue', {
398
455
  number: index + 1,
@@ -474,14 +531,11 @@ export function EventTriggerConfigEditor({
474
531
  })}
475
532
  disabled={disabled}
476
533
  onClick={() =>
477
- onChange({
478
- ...config,
479
- match: {
480
- ...config.match!,
481
- conditions: config.match!.conditions.filter(
482
- (_, current) => current !== index,
483
- ),
484
- },
534
+ changeMatch({
535
+ ...match!,
536
+ conditions: match!.conditions.filter(
537
+ (_, current) => current !== index,
538
+ ),
485
539
  })
486
540
  }
487
541
  >
@@ -500,15 +554,12 @@ export function EventTriggerConfigEditor({
500
554
  icon={<PlusOutlined />}
501
555
  disabled={disabled}
502
556
  onClick={() =>
503
- onChange({
504
- ...config,
505
- match: {
506
- ...config.match!,
507
- conditions: [
508
- ...config.match!.conditions,
509
- newMatchCondition(defaultMatchField),
510
- ],
511
- },
557
+ changeMatch({
558
+ ...match!,
559
+ conditions: [
560
+ ...match!.conditions,
561
+ newMatchCondition(defaultMatchField),
562
+ ],
512
563
  })
513
564
  }
514
565
  >
@@ -618,3 +669,14 @@ export function EventTriggerConfigEditor({
618
669
  </Space>
619
670
  );
620
671
  }
672
+
673
+ function parseValueList(value?: string): string[] {
674
+ try {
675
+ const parsed: unknown = JSON.parse(value ?? '[]');
676
+ return Array.isArray(parsed)
677
+ ? parsed.filter((item): item is string => typeof item === 'string')
678
+ : [];
679
+ } catch {
680
+ return [];
681
+ }
682
+ }