@open-mercato/core 0.4.2-canary-d0a025141f → 0.4.2-canary-3efa759f5c

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 (158) hide show
  1. package/dist/generated/entities.ids.generated.js +0 -1
  2. package/dist/generated/entities.ids.generated.js.map +2 -2
  3. package/dist/generated/entity-fields-registry.js +0 -2
  4. package/dist/generated/entity-fields-registry.js.map +2 -2
  5. package/dist/modules/business_rules/data/validators.js +0 -34
  6. package/dist/modules/business_rules/data/validators.js.map +2 -2
  7. package/dist/modules/business_rules/index.js +1 -21
  8. package/dist/modules/business_rules/index.js.map +2 -2
  9. package/dist/modules/business_rules/lib/rule-engine.js +1 -182
  10. package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
  11. package/dist/modules/sales/acl.js +0 -1
  12. package/dist/modules/sales/acl.js.map +2 -2
  13. package/dist/modules/sales/backend/sales/documents/[id]/page.js +0 -12
  14. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  15. package/dist/modules/sales/commands/documents.js +0 -62
  16. package/dist/modules/sales/commands/documents.js.map +2 -2
  17. package/dist/modules/sales/lib/dictionaries.js +0 -3
  18. package/dist/modules/sales/lib/dictionaries.js.map +2 -2
  19. package/dist/modules/workflows/acl.js +0 -2
  20. package/dist/modules/workflows/acl.js.map +2 -2
  21. package/dist/modules/workflows/api/instances/route.js +6 -18
  22. package/dist/modules/workflows/api/instances/route.js.map +2 -2
  23. package/dist/modules/workflows/api/tasks/route.js +1 -6
  24. package/dist/modules/workflows/api/tasks/route.js.map +2 -2
  25. package/dist/modules/workflows/backend/definitions/[id]/page.js +1 -9
  26. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  27. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js +1 -1
  28. package/dist/modules/workflows/backend/definitions/[id]/page.meta.js.map +2 -2
  29. package/dist/modules/workflows/backend/definitions/create/page.js +15 -24
  30. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  31. package/dist/modules/workflows/backend/definitions/create/page.meta.js +1 -1
  32. package/dist/modules/workflows/backend/definitions/create/page.meta.js.map +2 -2
  33. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +132 -150
  34. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  35. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js +1 -1
  36. package/dist/modules/workflows/backend/definitions/visual-editor/page.meta.js.map +2 -2
  37. package/dist/modules/workflows/backend/events/[id]/page.js +1 -1
  38. package/dist/modules/workflows/backend/events/[id]/page.js.map +2 -2
  39. package/dist/modules/workflows/backend/events/[id]/page.meta.js +2 -2
  40. package/dist/modules/workflows/backend/events/[id]/page.meta.js.map +2 -2
  41. package/dist/modules/workflows/backend/instances/[id]/page.meta.js +2 -2
  42. package/dist/modules/workflows/backend/instances/[id]/page.meta.js.map +2 -2
  43. package/dist/modules/workflows/backend/tasks/[id]/page.js +1 -1
  44. package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
  45. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js +2 -2
  46. package/dist/modules/workflows/backend/tasks/[id]/page.meta.js.map +2 -2
  47. package/dist/modules/workflows/backend/tasks/page.js +6 -5
  48. package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
  49. package/dist/modules/workflows/cli.js +3 -81
  50. package/dist/modules/workflows/cli.js.map +3 -3
  51. package/dist/modules/workflows/data/entities.js +1 -64
  52. package/dist/modules/workflows/data/entities.js.map +2 -2
  53. package/dist/modules/workflows/data/validators.js +0 -115
  54. package/dist/modules/workflows/data/validators.js.map +2 -2
  55. package/dist/modules/workflows/examples/checkout-demo-definition.json +5 -1
  56. package/dist/modules/workflows/lib/activity-executor.js +13 -75
  57. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  58. package/dist/modules/workflows/lib/graph-utils.js +2 -71
  59. package/dist/modules/workflows/lib/graph-utils.js.map +2 -2
  60. package/dist/modules/workflows/lib/seeds.js +5 -22
  61. package/dist/modules/workflows/lib/seeds.js.map +2 -2
  62. package/dist/modules/workflows/lib/start-validator.js +23 -33
  63. package/dist/modules/workflows/lib/start-validator.js.map +2 -2
  64. package/dist/modules/workflows/lib/transition-handler.js +45 -157
  65. package/dist/modules/workflows/lib/transition-handler.js.map +3 -3
  66. package/generated/entities.ids.generated.ts +0 -1
  67. package/generated/entity-fields-registry.ts +0 -2
  68. package/package.json +2 -2
  69. package/src/modules/business_rules/data/validators.ts +0 -40
  70. package/src/modules/business_rules/index.ts +0 -25
  71. package/src/modules/business_rules/lib/rule-engine.ts +1 -281
  72. package/src/modules/sales/acl.ts +0 -1
  73. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +0 -16
  74. package/src/modules/sales/commands/documents.ts +1 -74
  75. package/src/modules/sales/lib/dictionaries.ts +0 -3
  76. package/src/modules/workflows/acl.ts +0 -2
  77. package/src/modules/workflows/api/__tests__/instances.route.test.ts +2 -5
  78. package/src/modules/workflows/api/instances/route.ts +7 -21
  79. package/src/modules/workflows/api/tasks/route.ts +1 -7
  80. package/src/modules/workflows/backend/definitions/[id]/page.meta.ts +1 -1
  81. package/src/modules/workflows/backend/definitions/[id]/page.tsx +0 -9
  82. package/src/modules/workflows/backend/definitions/create/page.meta.ts +1 -1
  83. package/src/modules/workflows/backend/definitions/create/page.tsx +0 -9
  84. package/src/modules/workflows/backend/definitions/visual-editor/page.meta.ts +1 -1
  85. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +3 -21
  86. package/src/modules/workflows/backend/events/[id]/page.meta.ts +2 -2
  87. package/src/modules/workflows/backend/events/[id]/page.tsx +1 -1
  88. package/src/modules/workflows/backend/instances/[id]/page.meta.ts +2 -2
  89. package/src/modules/workflows/backend/tasks/[id]/page.meta.ts +2 -2
  90. package/src/modules/workflows/backend/tasks/[id]/page.tsx +1 -1
  91. package/src/modules/workflows/backend/tasks/page.tsx +6 -5
  92. package/src/modules/workflows/cli.ts +0 -111
  93. package/src/modules/workflows/data/entities.ts +0 -124
  94. package/src/modules/workflows/data/validators.ts +0 -138
  95. package/src/modules/workflows/examples/checkout-demo-definition.json +5 -1
  96. package/src/modules/workflows/i18n/en.json +0 -71
  97. package/src/modules/workflows/lib/__tests__/activity-executor.test.ts +36 -43
  98. package/src/modules/workflows/lib/__tests__/transition-handler.test.ts +90 -170
  99. package/src/modules/workflows/lib/activity-executor.ts +16 -129
  100. package/src/modules/workflows/lib/graph-utils.ts +2 -117
  101. package/src/modules/workflows/lib/seeds.ts +8 -34
  102. package/src/modules/workflows/lib/start-validator.ts +28 -38
  103. package/src/modules/workflows/lib/transition-handler.ts +55 -208
  104. package/dist/generated/entities/workflow_event_trigger/index.js +0 -33
  105. package/dist/generated/entities/workflow_event_trigger/index.js.map +0 -7
  106. package/dist/modules/auth/events.js +0 -30
  107. package/dist/modules/auth/events.js.map +0 -7
  108. package/dist/modules/business_rules/api/execute/[ruleId]/route.js +0 -145
  109. package/dist/modules/business_rules/api/execute/[ruleId]/route.js.map +0 -7
  110. package/dist/modules/catalog/events.js +0 -34
  111. package/dist/modules/catalog/events.js.map +0 -7
  112. package/dist/modules/customers/events.js +0 -49
  113. package/dist/modules/customers/events.js.map +0 -7
  114. package/dist/modules/directory/events.js +0 -23
  115. package/dist/modules/directory/events.js.map +0 -7
  116. package/dist/modules/sales/events.js +0 -63
  117. package/dist/modules/sales/events.js.map +0 -7
  118. package/dist/modules/sales/lib/frontend/documentDataEvents.js +0 -25
  119. package/dist/modules/sales/lib/frontend/documentDataEvents.js.map +0 -7
  120. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +0 -481
  121. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +0 -7
  122. package/dist/modules/workflows/components/EventTriggersEditor.js +0 -553
  123. package/dist/modules/workflows/components/EventTriggersEditor.js.map +0 -7
  124. package/dist/modules/workflows/events.js +0 -38
  125. package/dist/modules/workflows/events.js.map +0 -7
  126. package/dist/modules/workflows/examples/order-approval-definition.json +0 -257
  127. package/dist/modules/workflows/examples/order-approval-guard-rules.json +0 -32
  128. package/dist/modules/workflows/lib/event-trigger-service.js +0 -308
  129. package/dist/modules/workflows/lib/event-trigger-service.js.map +0 -7
  130. package/dist/modules/workflows/migrations/Migration20260123143500.js +0 -36
  131. package/dist/modules/workflows/migrations/Migration20260123143500.js.map +0 -7
  132. package/dist/modules/workflows/subscribers/event-trigger.js +0 -78
  133. package/dist/modules/workflows/subscribers/event-trigger.js.map +0 -7
  134. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js +0 -323
  135. package/dist/modules/workflows/widgets/injection/order-approval/widget.client.js.map +0 -7
  136. package/dist/modules/workflows/widgets/injection/order-approval/widget.js +0 -17
  137. package/dist/modules/workflows/widgets/injection/order-approval/widget.js.map +0 -7
  138. package/dist/modules/workflows/widgets/injection-table.js +0 -19
  139. package/dist/modules/workflows/widgets/injection-table.js.map +0 -7
  140. package/generated/entities/workflow_event_trigger/index.ts +0 -15
  141. package/src/modules/auth/events.ts +0 -39
  142. package/src/modules/business_rules/api/execute/[ruleId]/route.ts +0 -163
  143. package/src/modules/catalog/events.ts +0 -45
  144. package/src/modules/customers/events.ts +0 -63
  145. package/src/modules/directory/events.ts +0 -31
  146. package/src/modules/sales/events.ts +0 -82
  147. package/src/modules/sales/lib/frontend/documentDataEvents.ts +0 -28
  148. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +0 -581
  149. package/src/modules/workflows/components/EventTriggersEditor.tsx +0 -664
  150. package/src/modules/workflows/events.ts +0 -49
  151. package/src/modules/workflows/examples/order-approval-definition.json +0 -257
  152. package/src/modules/workflows/examples/order-approval-guard-rules.json +0 -32
  153. package/src/modules/workflows/lib/event-trigger-service.ts +0 -557
  154. package/src/modules/workflows/migrations/Migration20260123143500.ts +0 -38
  155. package/src/modules/workflows/subscribers/event-trigger.ts +0 -109
  156. package/src/modules/workflows/widgets/injection/order-approval/widget.client.tsx +0 -446
  157. package/src/modules/workflows/widgets/injection/order-approval/widget.ts +0 -16
  158. package/src/modules/workflows/widgets/injection-table.ts +0 -21
@@ -1,664 +0,0 @@
1
- "use client"
2
-
3
- import * as React from 'react'
4
- import { useState, useCallback } from 'react'
5
- import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
6
- import { Button } from '@open-mercato/ui/primitives/button'
7
- import { Input } from '@open-mercato/ui/primitives/input'
8
- import { Textarea } from '@open-mercato/ui/primitives/textarea'
9
- import { Label } from '@open-mercato/ui/primitives/label'
10
- import { Switch } from '@open-mercato/ui/primitives/switch'
11
- import { Spinner } from '@open-mercato/ui/primitives/spinner'
12
- import { Badge } from '@open-mercato/ui/primitives/badge'
13
- import {
14
- Dialog,
15
- DialogContent,
16
- DialogDescription,
17
- DialogFooter,
18
- DialogHeader,
19
- DialogTitle,
20
- } from '@open-mercato/ui/primitives/dialog'
21
- import { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'
22
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
23
- import { flash } from '@open-mercato/ui/backend/FlashMessages'
24
- import { EventSelect } from '@open-mercato/ui/backend/inputs/EventSelect'
25
- import { useT } from '@open-mercato/shared/lib/i18n/context'
26
- import { Plus, Trash2, Edit2, Zap, AlertCircle, X } from 'lucide-react'
27
-
28
- interface EventTrigger {
29
- id: string
30
- name: string
31
- description?: string | null
32
- eventPattern: string
33
- config?: {
34
- filterConditions?: Array<{
35
- field: string
36
- operator: string
37
- value: unknown
38
- }>
39
- contextMapping?: Array<{
40
- targetKey: string
41
- sourceExpression: string
42
- defaultValue?: unknown
43
- }>
44
- debounceMs?: number
45
- maxConcurrentInstances?: number
46
- } | null
47
- enabled: boolean
48
- priority: number
49
- workflowDefinitionId: string
50
- createdAt: string
51
- updatedAt: string
52
- }
53
-
54
- interface EventTriggersEditorProps {
55
- workflowDefinitionId: string
56
- workflowId?: string
57
- className?: string
58
- }
59
-
60
- const FILTER_OPERATORS = [
61
- { value: 'eq', label: 'Equals' },
62
- { value: 'neq', label: 'Not Equals' },
63
- { value: 'gt', label: 'Greater Than' },
64
- { value: 'gte', label: 'Greater Than or Equal' },
65
- { value: 'lt', label: 'Less Than' },
66
- { value: 'lte', label: 'Less Than or Equal' },
67
- { value: 'contains', label: 'Contains' },
68
- { value: 'startsWith', label: 'Starts With' },
69
- { value: 'endsWith', label: 'Ends With' },
70
- { value: 'in', label: 'In (array)' },
71
- { value: 'notIn', label: 'Not In (array)' },
72
- { value: 'exists', label: 'Exists' },
73
- { value: 'notExists', label: 'Not Exists' },
74
- { value: 'regex', label: 'Regex Match' },
75
- ]
76
-
77
- type TriggerFormValues = {
78
- name: string
79
- description: string
80
- eventPattern: string
81
- enabled: boolean
82
- priority: number
83
- filterConditions: Array<{ field: string; operator: string; value: string }>
84
- contextMappings: Array<{ targetKey: string; sourceExpression: string; defaultValue: string }>
85
- debounceMs: string
86
- maxConcurrentInstances: string
87
- }
88
-
89
- const defaultFormValues: TriggerFormValues = {
90
- name: '',
91
- description: '',
92
- eventPattern: '',
93
- enabled: true,
94
- priority: 0,
95
- filterConditions: [],
96
- contextMappings: [],
97
- debounceMs: '',
98
- maxConcurrentInstances: '',
99
- }
100
-
101
- export function EventTriggersEditor({
102
- workflowDefinitionId,
103
- workflowId,
104
- className,
105
- }: EventTriggersEditorProps) {
106
- const t = useT()
107
- const queryClient = useQueryClient()
108
- const [showDialog, setShowDialog] = useState(false)
109
- const [editingTrigger, setEditingTrigger] = useState<EventTrigger | null>(null)
110
- const [formValues, setFormValues] = useState<TriggerFormValues>(defaultFormValues)
111
- const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null)
112
-
113
- // Fetch triggers for this workflow definition
114
- const { data: triggersData, isLoading } = useQuery({
115
- queryKey: ['workflow-triggers', workflowDefinitionId],
116
- queryFn: async () => {
117
- const result = await apiCall<{ data: EventTrigger[]; pagination: any }>(
118
- `/api/workflows/triggers?workflowDefinitionId=${workflowDefinitionId}&limit=100`
119
- )
120
- if (!result.ok) {
121
- throw new Error('Failed to load triggers')
122
- }
123
- return result.result?.data || []
124
- },
125
- enabled: !!workflowDefinitionId,
126
- })
127
-
128
- const triggers = triggersData || []
129
-
130
- // Create trigger mutation
131
- const createMutation = useMutation({
132
- mutationFn: async (values: TriggerFormValues) => {
133
- const payload = buildTriggerPayload(values)
134
- const result = await apiCall<{ data: EventTrigger; error?: string }>(
135
- '/api/workflows/triggers',
136
- {
137
- method: 'POST',
138
- headers: { 'Content-Type': 'application/json' },
139
- body: JSON.stringify({
140
- ...payload,
141
- workflowDefinitionId,
142
- }),
143
- }
144
- )
145
- if (!result.ok) {
146
- throw new Error(result.result?.error || 'Failed to create trigger')
147
- }
148
- return result.result?.data
149
- },
150
- onSuccess: () => {
151
- queryClient.invalidateQueries({ queryKey: ['workflow-triggers', workflowDefinitionId] })
152
- flash('Event trigger created successfully', 'success')
153
- handleCloseDialog()
154
- },
155
- onError: (error: Error) => {
156
- flash(error.message, 'error')
157
- },
158
- })
159
-
160
- // Update trigger mutation
161
- const updateMutation = useMutation({
162
- mutationFn: async ({ id, values }: { id: string; values: TriggerFormValues }) => {
163
- const payload = buildTriggerPayload(values)
164
- const result = await apiCall<{ data: EventTrigger; error?: string }>(
165
- `/api/workflows/triggers/${id}`,
166
- {
167
- method: 'PUT',
168
- headers: { 'Content-Type': 'application/json' },
169
- body: JSON.stringify(payload),
170
- }
171
- )
172
- if (!result.ok) {
173
- throw new Error(result.result?.error || 'Failed to update trigger')
174
- }
175
- return result.result?.data
176
- },
177
- onSuccess: () => {
178
- queryClient.invalidateQueries({ queryKey: ['workflow-triggers', workflowDefinitionId] })
179
- flash('Event trigger updated successfully', 'success')
180
- handleCloseDialog()
181
- },
182
- onError: (error: Error) => {
183
- flash(error.message, 'error')
184
- },
185
- })
186
-
187
- // Delete trigger mutation
188
- const deleteMutation = useMutation({
189
- mutationFn: async (id: string) => {
190
- const result = await apiCall<{ error?: string }>(
191
- `/api/workflows/triggers/${id}`,
192
- { method: 'DELETE' }
193
- )
194
- if (!result.ok) {
195
- throw new Error(result.result?.error || 'Failed to delete trigger')
196
- }
197
- },
198
- onSuccess: () => {
199
- queryClient.invalidateQueries({ queryKey: ['workflow-triggers', workflowDefinitionId] })
200
- flash('Event trigger deleted successfully', 'success')
201
- setDeleteConfirmId(null)
202
- },
203
- onError: (error: Error) => {
204
- flash(error.message, 'error')
205
- },
206
- })
207
-
208
- // Build API payload from form values
209
- const buildTriggerPayload = useCallback((values: TriggerFormValues) => {
210
- const config: EventTrigger['config'] = {}
211
-
212
- if (values.filterConditions.length > 0) {
213
- config.filterConditions = values.filterConditions.map(fc => ({
214
- field: fc.field,
215
- operator: fc.operator,
216
- value: parseConditionValue(fc.value),
217
- }))
218
- }
219
-
220
- if (values.contextMappings.length > 0) {
221
- config.contextMapping = values.contextMappings.map(cm => ({
222
- targetKey: cm.targetKey,
223
- sourceExpression: cm.sourceExpression,
224
- defaultValue: cm.defaultValue ? parseConditionValue(cm.defaultValue) : undefined,
225
- }))
226
- }
227
-
228
- if (values.debounceMs) {
229
- config.debounceMs = parseInt(values.debounceMs, 10)
230
- }
231
-
232
- if (values.maxConcurrentInstances) {
233
- config.maxConcurrentInstances = parseInt(values.maxConcurrentInstances, 10)
234
- }
235
-
236
- return {
237
- name: values.name,
238
- description: values.description || null,
239
- eventPattern: values.eventPattern,
240
- enabled: values.enabled,
241
- priority: values.priority,
242
- config: Object.keys(config).length > 0 ? config : null,
243
- }
244
- }, [])
245
-
246
- // Parse condition value (try JSON, fallback to string)
247
- const parseConditionValue = (value: string): unknown => {
248
- try {
249
- return JSON.parse(value)
250
- } catch {
251
- return value
252
- }
253
- }
254
-
255
- // Open dialog for creating new trigger
256
- const handleCreateNew = useCallback(() => {
257
- setEditingTrigger(null)
258
- setFormValues(defaultFormValues)
259
- setShowDialog(true)
260
- }, [])
261
-
262
- // Open dialog for editing trigger
263
- const handleEdit = useCallback((trigger: EventTrigger) => {
264
- setEditingTrigger(trigger)
265
- setFormValues({
266
- name: trigger.name,
267
- description: trigger.description || '',
268
- eventPattern: trigger.eventPattern,
269
- enabled: trigger.enabled,
270
- priority: trigger.priority,
271
- filterConditions: trigger.config?.filterConditions?.map(fc => ({
272
- field: fc.field,
273
- operator: fc.operator,
274
- value: typeof fc.value === 'string' ? fc.value : JSON.stringify(fc.value),
275
- })) || [],
276
- contextMappings: trigger.config?.contextMapping?.map(cm => ({
277
- targetKey: cm.targetKey,
278
- sourceExpression: cm.sourceExpression,
279
- defaultValue: cm.defaultValue !== undefined
280
- ? (typeof cm.defaultValue === 'string' ? cm.defaultValue : JSON.stringify(cm.defaultValue))
281
- : '',
282
- })) || [],
283
- debounceMs: trigger.config?.debounceMs?.toString() || '',
284
- maxConcurrentInstances: trigger.config?.maxConcurrentInstances?.toString() || '',
285
- })
286
- setShowDialog(true)
287
- }, [])
288
-
289
- // Close dialog
290
- const handleCloseDialog = useCallback(() => {
291
- setShowDialog(false)
292
- setEditingTrigger(null)
293
- setFormValues(defaultFormValues)
294
- }, [])
295
-
296
- // Submit form
297
- const handleSubmit = useCallback(() => {
298
- if (!formValues.name.trim()) {
299
- flash('Name is required', 'error')
300
- return
301
- }
302
- if (!formValues.eventPattern.trim()) {
303
- flash('Event pattern is required', 'error')
304
- return
305
- }
306
-
307
- if (editingTrigger) {
308
- updateMutation.mutate({ id: editingTrigger.id, values: formValues })
309
- } else {
310
- createMutation.mutate(formValues)
311
- }
312
- }, [formValues, editingTrigger, createMutation, updateMutation])
313
-
314
- // Add filter condition
315
- const addFilterCondition = useCallback(() => {
316
- setFormValues(prev => ({
317
- ...prev,
318
- filterConditions: [...prev.filterConditions, { field: '', operator: 'eq', value: '' }],
319
- }))
320
- }, [])
321
-
322
- // Remove filter condition
323
- const removeFilterCondition = useCallback((index: number) => {
324
- setFormValues(prev => ({
325
- ...prev,
326
- filterConditions: prev.filterConditions.filter((_, i) => i !== index),
327
- }))
328
- }, [])
329
-
330
- // Update filter condition
331
- const updateFilterCondition = useCallback((index: number, field: string, value: string) => {
332
- setFormValues(prev => ({
333
- ...prev,
334
- filterConditions: prev.filterConditions.map((fc, i) =>
335
- i === index ? { ...fc, [field]: value } : fc
336
- ),
337
- }))
338
- }, [])
339
-
340
- // Add context mapping
341
- const addContextMapping = useCallback(() => {
342
- setFormValues(prev => ({
343
- ...prev,
344
- contextMappings: [...prev.contextMappings, { targetKey: '', sourceExpression: '', defaultValue: '' }],
345
- }))
346
- }, [])
347
-
348
- // Remove context mapping
349
- const removeContextMapping = useCallback((index: number) => {
350
- setFormValues(prev => ({
351
- ...prev,
352
- contextMappings: prev.contextMappings.filter((_, i) => i !== index),
353
- }))
354
- }, [])
355
-
356
- // Update context mapping
357
- const updateContextMapping = useCallback((index: number, field: string, value: string) => {
358
- setFormValues(prev => ({
359
- ...prev,
360
- contextMappings: prev.contextMappings.map((cm, i) =>
361
- i === index ? { ...cm, [field]: value } : cm
362
- ),
363
- }))
364
- }, [])
365
-
366
- const isSaving = createMutation.isPending || updateMutation.isPending
367
-
368
- return (
369
- <div className={className}>
370
- <div className="rounded-lg border bg-card p-4">
371
- <div className="flex items-center justify-between mb-4">
372
- <div className="flex items-center gap-2">
373
- <Zap className="w-5 h-5 text-amber-500" />
374
- <h3 className="text-sm font-semibold uppercase text-muted-foreground">Event Triggers</h3>
375
- </div>
376
- <Button size="sm" variant="outline" onClick={handleCreateNew}>
377
- <Plus className="w-4 h-4 mr-1" />
378
- Add Trigger
379
- </Button>
380
- </div>
381
-
382
- <p className="text-xs text-muted-foreground mb-4">
383
- Configure events that automatically start this workflow. When a matching event occurs in the system,
384
- a new workflow instance will be created with the mapped context.
385
- </p>
386
-
387
- {isLoading ? (
388
- <div className="flex items-center justify-center py-8">
389
- <Spinner className="w-6 h-6" />
390
- </div>
391
- ) : triggers.length === 0 ? (
392
- <Alert variant="info">
393
- <AlertCircle className="w-4 h-4" />
394
- <AlertTitle>No triggers configured</AlertTitle>
395
- <AlertDescription>
396
- Click "Add Trigger" to create an event trigger that automatically starts this workflow.
397
- </AlertDescription>
398
- </Alert>
399
- ) : (
400
- <div className="space-y-2">
401
- {triggers.map(trigger => (
402
- <div
403
- key={trigger.id}
404
- className="flex items-center justify-between p-3 rounded-lg border bg-background hover:bg-accent/50 transition-colors"
405
- >
406
- <div className="flex items-center gap-3">
407
- <Badge variant={trigger.enabled ? 'default' : 'secondary'}>
408
- {trigger.enabled ? 'Active' : 'Disabled'}
409
- </Badge>
410
- <div>
411
- <div className="font-medium text-sm">{trigger.name}</div>
412
- <code className="text-xs text-muted-foreground">{trigger.eventPattern}</code>
413
- </div>
414
- </div>
415
- <div className="flex items-center gap-2">
416
- <Button size="sm" variant="ghost" onClick={() => handleEdit(trigger)}>
417
- <Edit2 className="w-4 h-4" />
418
- </Button>
419
- <Button
420
- size="sm"
421
- variant="ghost"
422
- className="text-destructive hover:text-destructive"
423
- onClick={() => setDeleteConfirmId(trigger.id)}
424
- >
425
- <Trash2 className="w-4 h-4" />
426
- </Button>
427
- </div>
428
- </div>
429
- ))}
430
- </div>
431
- )}
432
- </div>
433
-
434
- {/* Create/Edit Dialog */}
435
- <Dialog open={showDialog} onOpenChange={setShowDialog}>
436
- <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
437
- <DialogHeader>
438
- <DialogTitle>
439
- {editingTrigger ? 'Edit Event Trigger' : 'Create Event Trigger'}
440
- </DialogTitle>
441
- <DialogDescription>
442
- Configure when this workflow should be automatically started based on system events.
443
- </DialogDescription>
444
- </DialogHeader>
445
-
446
- <div className="space-y-4 py-4">
447
- {/* Basic Info */}
448
- <div className="grid grid-cols-2 gap-4">
449
- <div className="space-y-1">
450
- <Label htmlFor="trigger-name">Name *</Label>
451
- <Input
452
- id="trigger-name"
453
- value={formValues.name}
454
- onChange={e => setFormValues(prev => ({ ...prev, name: e.target.value }))}
455
- placeholder="Order Approval Trigger"
456
- />
457
- </div>
458
- <div className="space-y-1">
459
- <Label htmlFor="trigger-priority">Priority</Label>
460
- <Input
461
- id="trigger-priority"
462
- type="number"
463
- value={formValues.priority}
464
- onChange={e => setFormValues(prev => ({ ...prev, priority: parseInt(e.target.value) || 0 }))}
465
- placeholder="0"
466
- />
467
- <p className="text-xs text-muted-foreground">Higher priority triggers execute first</p>
468
- </div>
469
- </div>
470
-
471
- <div className="space-y-1">
472
- <Label htmlFor="trigger-description">Description</Label>
473
- <Textarea
474
- id="trigger-description"
475
- value={formValues.description}
476
- onChange={e => setFormValues(prev => ({ ...prev, description: e.target.value }))}
477
- placeholder="Describe when this trigger should fire..."
478
- rows={2}
479
- />
480
- </div>
481
-
482
- {/* Event Pattern */}
483
- <div className="space-y-1">
484
- <Label htmlFor="trigger-pattern">Event Pattern *</Label>
485
- <div className="flex gap-2">
486
- <Input
487
- id="trigger-pattern"
488
- value={formValues.eventPattern}
489
- onChange={e => setFormValues(prev => ({ ...prev, eventPattern: e.target.value }))}
490
- placeholder="sales.orders.updated"
491
- className="flex-1"
492
- />
493
- <EventSelect
494
- value=""
495
- onChange={(eventId) => setFormValues(prev => ({ ...prev, eventPattern: eventId }))}
496
- placeholder="Quick select..."
497
- className="w-[200px]"
498
- />
499
- </div>
500
- <p className="text-xs text-muted-foreground">
501
- Use * as wildcard: "sales.orders.*" matches any order event
502
- </p>
503
- </div>
504
-
505
- {/* Enabled Switch */}
506
- <div className="flex items-center gap-2">
507
- <Switch
508
- id="trigger-enabled"
509
- checked={formValues.enabled}
510
- onCheckedChange={checked => setFormValues(prev => ({ ...prev, enabled: checked }))}
511
- />
512
- <Label htmlFor="trigger-enabled">Enabled</Label>
513
- </div>
514
-
515
- {/* Filter Conditions */}
516
- <div className="space-y-2">
517
- <div className="flex items-center justify-between">
518
- <Label>Filter Conditions</Label>
519
- <Button size="sm" variant="ghost" onClick={addFilterCondition}>
520
- <Plus className="w-4 h-4 mr-1" />
521
- Add Condition
522
- </Button>
523
- </div>
524
- <p className="text-xs text-muted-foreground">
525
- Only trigger when the event payload matches these conditions (all must match)
526
- </p>
527
- {formValues.filterConditions.map((fc, index) => (
528
- <div key={index} className="flex items-center gap-2">
529
- <Input
530
- value={fc.field}
531
- onChange={e => updateFilterCondition(index, 'field', e.target.value)}
532
- placeholder="status"
533
- className="w-1/3"
534
- />
535
- <select
536
- value={fc.operator}
537
- onChange={(e: React.ChangeEvent<HTMLSelectElement>) => updateFilterCondition(index, 'operator', e.target.value)}
538
- className="h-10 w-[140px] rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
539
- >
540
- {FILTER_OPERATORS.map(op => (
541
- <option key={op.value} value={op.value}>
542
- {op.label}
543
- </option>
544
- ))}
545
- </select>
546
- <Input
547
- value={fc.value}
548
- onChange={e => updateFilterCondition(index, 'value', e.target.value)}
549
- placeholder="submitted"
550
- className="flex-1"
551
- />
552
- <Button size="icon" variant="ghost" onClick={() => removeFilterCondition(index)}>
553
- <X className="w-4 h-4" />
554
- </Button>
555
- </div>
556
- ))}
557
- </div>
558
-
559
- {/* Context Mapping */}
560
- <div className="space-y-2">
561
- <div className="flex items-center justify-between">
562
- <Label>Context Mapping</Label>
563
- <Button size="sm" variant="ghost" onClick={addContextMapping}>
564
- <Plus className="w-4 h-4 mr-1" />
565
- Add Mapping
566
- </Button>
567
- </div>
568
- <p className="text-xs text-muted-foreground">
569
- Map values from the event payload to the workflow's initial context
570
- </p>
571
- {formValues.contextMappings.map((cm, index) => (
572
- <div key={index} className="flex items-center gap-2">
573
- <Input
574
- value={cm.targetKey}
575
- onChange={e => updateContextMapping(index, 'targetKey', e.target.value)}
576
- placeholder="orderId"
577
- className="w-1/3"
578
- />
579
- <span className="text-muted-foreground">=</span>
580
- <Input
581
- value={cm.sourceExpression}
582
- onChange={e => updateContextMapping(index, 'sourceExpression', e.target.value)}
583
- placeholder="id"
584
- className="flex-1"
585
- />
586
- <Input
587
- value={cm.defaultValue}
588
- onChange={e => updateContextMapping(index, 'defaultValue', e.target.value)}
589
- placeholder="default"
590
- className="w-24"
591
- />
592
- <Button size="icon" variant="ghost" onClick={() => removeContextMapping(index)}>
593
- <X className="w-4 h-4" />
594
- </Button>
595
- </div>
596
- ))}
597
- </div>
598
-
599
- {/* Advanced Options */}
600
- <div className="grid grid-cols-2 gap-4">
601
- <div className="space-y-1">
602
- <Label htmlFor="trigger-debounce">Debounce (ms)</Label>
603
- <Input
604
- id="trigger-debounce"
605
- type="number"
606
- value={formValues.debounceMs}
607
- onChange={e => setFormValues(prev => ({ ...prev, debounceMs: e.target.value }))}
608
- placeholder="0"
609
- />
610
- <p className="text-xs text-muted-foreground">Delay to prevent rapid re-triggers</p>
611
- </div>
612
- <div className="space-y-1">
613
- <Label htmlFor="trigger-max-concurrent">Max Concurrent Instances</Label>
614
- <Input
615
- id="trigger-max-concurrent"
616
- type="number"
617
- value={formValues.maxConcurrentInstances}
618
- onChange={e => setFormValues(prev => ({ ...prev, maxConcurrentInstances: e.target.value }))}
619
- placeholder="Unlimited"
620
- />
621
- <p className="text-xs text-muted-foreground">Limit simultaneous workflow instances</p>
622
- </div>
623
- </div>
624
- </div>
625
-
626
- <DialogFooter>
627
- <Button variant="outline" onClick={handleCloseDialog} disabled={isSaving}>
628
- Cancel
629
- </Button>
630
- <Button onClick={handleSubmit} disabled={isSaving}>
631
- {isSaving ? <Spinner className="w-4 h-4 mr-2" /> : null}
632
- {editingTrigger ? 'Update' : 'Create'}
633
- </Button>
634
- </DialogFooter>
635
- </DialogContent>
636
- </Dialog>
637
-
638
- {/* Delete Confirmation Dialog */}
639
- <Dialog open={!!deleteConfirmId} onOpenChange={() => setDeleteConfirmId(null)}>
640
- <DialogContent>
641
- <DialogHeader>
642
- <DialogTitle>Delete Event Trigger?</DialogTitle>
643
- <DialogDescription>
644
- This will permanently delete the event trigger. Workflows will no longer be automatically started by this trigger.
645
- </DialogDescription>
646
- </DialogHeader>
647
- <DialogFooter>
648
- <Button variant="outline" onClick={() => setDeleteConfirmId(null)}>
649
- Cancel
650
- </Button>
651
- <Button
652
- variant="destructive"
653
- onClick={() => deleteConfirmId && deleteMutation.mutate(deleteConfirmId)}
654
- disabled={deleteMutation.isPending}
655
- >
656
- {deleteMutation.isPending ? <Spinner className="w-4 h-4 mr-2" /> : null}
657
- Delete
658
- </Button>
659
- </DialogFooter>
660
- </DialogContent>
661
- </Dialog>
662
- </div>
663
- )
664
- }