@gadmin2n/schematics 0.0.148 → 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 (23) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +1 -1
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +66 -49
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +4 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.service.ts +31 -4
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-catalog.spec.ts +166 -17
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-trigger.pg.spec.ts +82 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.dto.ts +7 -2
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +91 -36
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.spec.ts +57 -0
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +11 -3
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +11 -3
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +41 -1
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/RegisterBusinessEventModal.test.tsx +46 -5
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/RegisterBusinessEventModal.tsx +14 -10
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.test.tsx +1 -1
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.tsx +2 -1
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.test.tsx +1 -1
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.tsx +2 -2
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.test.tsx +140 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.tsx +123 -60
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SimplifiedNodeEditors.test.tsx +31 -14
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/event-catalog.api.ts +4 -2
  23. package/package.json +1 -1
@@ -74,7 +74,7 @@ export default function BusinessEventCatalogPage() {
74
74
  event.label,
75
75
  event.schemaName,
76
76
  event.tableName,
77
- event.operation,
77
+ ...event.operations,
78
78
  ...event.fields.flatMap((field) => [
79
79
  field.path,
80
80
  field.label,
@@ -105,7 +105,7 @@ export default function BusinessEventCatalogPage() {
105
105
  {event.label}
106
106
  </Typography.Link>
107
107
  <Typography.Text code>
108
- {event.operation === 'UPDATE'
108
+ {event.operations.includes('UPDATE')
109
109
  ? event.watchedFields.join(', ')
110
110
  : t('eventCatalog.registration.allFields')}
111
111
  </Typography.Text>
@@ -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
 
@@ -276,11 +303,23 @@ export function EventTriggerConfigEditor({
276
303
 
277
304
  <Card size="small" title={t('nodeEditors.event.startCondition')}>
278
305
  <Radio.Group
279
- 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'}
280
318
  disabled={disabled || !selectedEvent}
281
319
  onChange={(event) => changeMatchMode(event.target.value)}
282
320
  >
283
321
  <Space direction="vertical" size={8}>
322
+ <Radio value="NEVER">{t('nodeEditors.event.neverStart')}</Radio>
284
323
  <Radio value="ALWAYS">{t('nodeEditors.event.alwaysStart')}</Radio>
285
324
  <Radio value="CONDITIONAL">
286
325
  {t('nodeEditors.event.conditionalStart')}
@@ -288,23 +327,20 @@ export function EventTriggerConfigEditor({
288
327
  </Space>
289
328
  </Radio.Group>
290
329
 
291
- {config.match && (
330
+ {match && (
292
331
  <Space
293
332
  direction="vertical"
294
333
  size={10}
295
334
  style={{ width: '100%', marginTop: 14 }}
296
335
  >
297
- {config.match.conditions.length > 1 && (
336
+ {match.conditions.length > 1 && (
298
337
  <Radio.Group
299
- value={config.match.combineOperation}
338
+ value={match.combineOperation}
300
339
  disabled={disabled}
301
340
  onChange={(event) =>
302
- onChange({
303
- ...config,
304
- match: {
305
- ...config.match!,
306
- combineOperation: event.target.value,
307
- },
341
+ changeMatch({
342
+ ...match!,
343
+ combineOperation: event.target.value,
308
344
  })
309
345
  }
310
346
  >
@@ -317,7 +353,7 @@ export function EventTriggerConfigEditor({
317
353
  </Radio.Group>
318
354
  )}
319
355
 
320
- {config.match.conditions.map((condition, index) => {
356
+ {match.conditions.map((condition, index) => {
321
357
  const selectedField = matchFields.find(
322
358
  (field) => field.path === condition.left.path,
323
359
  );
@@ -355,7 +391,8 @@ export function EventTriggerConfigEditor({
355
391
  : {
356
392
  source: 'FIXED',
357
393
  valueType: fixedValueType(field),
358
- value: '',
394
+ value:
395
+ condition.operator === 'IN' ? '[]' : '',
359
396
  },
360
397
  });
361
398
  }}
@@ -368,7 +405,7 @@ export function EventTriggerConfigEditor({
368
405
  value={condition.operator}
369
406
  disabled={disabled}
370
407
  options={(
371
- ['EQ', 'NEQ', 'EMPTY', 'NOT_EMPTY'] as const
408
+ ['EQ', 'NEQ', 'IN', 'EMPTY', 'NOT_EMPTY'] as const
372
409
  ).map((operator) => ({
373
410
  value: operator,
374
411
  label: t(
@@ -381,17 +418,38 @@ export function EventTriggerConfigEditor({
381
418
  operator,
382
419
  right: UNARY_MATCH_OPERATORS.has(operator)
383
420
  ? undefined
384
- : (condition.right ?? {
421
+ : {
385
422
  source: 'FIXED',
386
423
  valueType: fixedValueType(selectedField),
387
- value: '',
388
- }),
424
+ value: operator === 'IN' ? '[]' : '',
425
+ },
389
426
  })
390
427
  }
391
428
  style={{ width: '30%' }}
392
429
  />
393
430
  {!UNARY_MATCH_OPERATORS.has(condition.operator) &&
394
- (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' ? (
395
453
  <Select
396
454
  aria-label={t('nodeEditors.event.conditionValue', {
397
455
  number: index + 1,
@@ -473,14 +531,11 @@ export function EventTriggerConfigEditor({
473
531
  })}
474
532
  disabled={disabled}
475
533
  onClick={() =>
476
- onChange({
477
- ...config,
478
- match: {
479
- ...config.match!,
480
- conditions: config.match!.conditions.filter(
481
- (_, current) => current !== index,
482
- ),
483
- },
534
+ changeMatch({
535
+ ...match!,
536
+ conditions: match!.conditions.filter(
537
+ (_, current) => current !== index,
538
+ ),
484
539
  })
485
540
  }
486
541
  >
@@ -499,15 +554,12 @@ export function EventTriggerConfigEditor({
499
554
  icon={<PlusOutlined />}
500
555
  disabled={disabled}
501
556
  onClick={() =>
502
- onChange({
503
- ...config,
504
- match: {
505
- ...config.match!,
506
- conditions: [
507
- ...config.match!.conditions,
508
- newMatchCondition(defaultMatchField),
509
- ],
510
- },
557
+ changeMatch({
558
+ ...match!,
559
+ conditions: [
560
+ ...match!.conditions,
561
+ newMatchCondition(defaultMatchField),
562
+ ],
511
563
  })
512
564
  }
513
565
  >
@@ -617,3 +669,14 @@ export function EventTriggerConfigEditor({
617
669
  </Space>
618
670
  );
619
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
+ }
@@ -39,7 +39,7 @@ vi.mock('./event-catalog.api', () => ({
39
39
  label: '用户创建',
40
40
  schemaName: 'public',
41
41
  tableName: 't_user',
42
- operation: 'INSERT',
42
+ operations: ['INSERT'],
43
43
  watchedFields: ['id'],
44
44
  primaryKeyFields: ['id'],
45
45
  isEnabled: true,
@@ -68,7 +68,7 @@ vi.mock('./event-catalog.api', () => ({
68
68
  label: '硬件资产报废申请',
69
69
  schemaName: 'public',
70
70
  tableName: 't_hardware_asset',
71
- operation: 'UPDATE',
71
+ operations: ['UPDATE'],
72
72
  watchedFields: ['status'],
73
73
  primaryKeyFields: ['id'],
74
74
  isEnabled: true,
@@ -412,7 +412,11 @@ describe('simplified workflow node editors', () => {
412
412
  const onChange = vi.fn();
413
413
  render(
414
414
  <EventTriggerConfigEditor
415
- value={{ registrationCode: '', processingMode: 'PARALLEL' }}
415
+ value={{
416
+ registrationCode: '',
417
+ operationRules: [],
418
+ processingMode: 'PARALLEL',
419
+ }}
416
420
  onChange={onChange}
417
421
  schema={schema}
418
422
  />,
@@ -425,6 +429,7 @@ describe('simplified workflow node editors', () => {
425
429
 
426
430
  expect(onChange).toHaveBeenCalledWith({
427
431
  registrationCode: 'user-account-created',
432
+ operationRules: [{ operation: 'INSERT' }],
428
433
  processingMode: 'PARALLEL',
429
434
  });
430
435
  });
@@ -434,7 +439,11 @@ describe('simplified workflow node editors', () => {
434
439
  const onChange = vi.fn();
435
440
  render(
436
441
  <EventTriggerConfigEditor
437
- value={{ registrationCode: '', processingMode: 'PARALLEL' }}
442
+ value={{
443
+ registrationCode: '',
444
+ operationRules: [],
445
+ processingMode: 'PARALLEL',
446
+ }}
438
447
  onChange={onChange}
439
448
  schema={schema}
440
449
  />,
@@ -447,6 +456,7 @@ describe('simplified workflow node editors', () => {
447
456
 
448
457
  expect(onChange).toHaveBeenCalledWith({
449
458
  registrationCode: 'asset-disposal-submitted',
459
+ operationRules: [{ operation: 'UPDATE' }],
450
460
  processingMode: 'PARALLEL',
451
461
  });
452
462
  });
@@ -457,6 +467,7 @@ describe('simplified workflow node editors', () => {
457
467
  <EventTriggerConfigEditor
458
468
  value={{
459
469
  registrationCode: 'asset-disposal-submitted',
470
+ operationRules: [{ operation: 'UPDATE' }],
460
471
  processingMode: 'PARALLEL',
461
472
  }}
462
473
  onChange={vi.fn()}
@@ -479,6 +490,7 @@ describe('simplified workflow node editors', () => {
479
490
  <EventTriggerConfigEditor
480
491
  value={{
481
492
  registrationCode: 'asset-disposal-submitted',
493
+ operationRules: [{ operation: 'UPDATE' }],
482
494
  processingMode: 'PARALLEL',
483
495
  }}
484
496
  onChange={onChange}
@@ -491,17 +503,22 @@ describe('simplified workflow node editors', () => {
491
503
  expect(onChange).toHaveBeenLastCalledWith({
492
504
  registrationCode: 'asset-disposal-submitted',
493
505
  processingMode: 'PARALLEL',
494
- match: {
495
- combineOperation: 'AND',
496
- conditions: [
497
- {
498
- id: expect.stringMatching(/^condition-/),
499
- left: { source: 'WORKFLOW', path: 'after.status' },
500
- operator: 'EQ',
501
- right: { source: 'FIXED', valueType: 'STRING', value: '' },
506
+ operationRules: [
507
+ {
508
+ operation: 'UPDATE',
509
+ match: {
510
+ combineOperation: 'AND',
511
+ conditions: [
512
+ {
513
+ id: expect.stringMatching(/^condition-/),
514
+ left: { source: 'WORKFLOW', path: 'after.status' },
515
+ operator: 'EQ',
516
+ right: { source: 'FIXED', valueType: 'STRING', value: '' },
517
+ },
518
+ ],
502
519
  },
503
- ],
504
- },
520
+ },
521
+ ],
505
522
  });
506
523
  });
507
524
 
@@ -1,5 +1,7 @@
1
1
  import { customRequest } from 'helpers/http';
2
2
 
3
+ export type BusinessEventOperation = 'INSERT' | 'UPDATE' | 'DELETE';
4
+
3
5
  export interface BusinessEventField {
4
6
  path: string;
5
7
  label: string;
@@ -15,7 +17,7 @@ export interface BusinessEventOption {
15
17
  label: string;
16
18
  schemaName: string;
17
19
  tableName: string;
18
- operation: 'INSERT' | 'UPDATE' | 'DELETE';
20
+ operations: BusinessEventOperation[];
19
21
  watchedFields: string[];
20
22
  primaryKeyFields: string[];
21
23
  isEnabled: boolean;
@@ -52,7 +54,7 @@ export interface BusinessEventInstanceItem {
52
54
  export interface BusinessEventRegistrationInput {
53
55
  schemaName: string;
54
56
  tableName: string;
55
- operation: 'INSERT' | 'UPDATE' | 'DELETE';
57
+ operations: BusinessEventOperation[];
56
58
  watchedFields: string[];
57
59
  }
58
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gadmin2n/schematics",
3
- "version": "0.0.148",
3
+ "version": "0.0.149",
4
4
  "description": "Gadmin - modern, fast, powerful node.js web framework (@schematics)",
5
5
  "main": "dist/index.js",
6
6
  "files": [