@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,581 +0,0 @@
1
- "use client"
2
-
3
- import * as React from 'react'
4
- import { useState, useCallback } from 'react'
5
- import { Button } from '@open-mercato/ui/primitives/button'
6
- import { Input } from '@open-mercato/ui/primitives/input'
7
- import { Textarea } from '@open-mercato/ui/primitives/textarea'
8
- import { Label } from '@open-mercato/ui/primitives/label'
9
- import { Switch } from '@open-mercato/ui/primitives/switch'
10
- import { Badge } from '@open-mercato/ui/primitives/badge'
11
- import {
12
- Dialog,
13
- DialogContent,
14
- DialogDescription,
15
- DialogFooter,
16
- DialogHeader,
17
- DialogTitle,
18
- } from '@open-mercato/ui/primitives/dialog'
19
- import { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'
20
- import { EventSelect } from '@open-mercato/ui/backend/inputs/EventSelect'
21
- import { useT } from '@open-mercato/shared/lib/i18n/context'
22
- import { Plus, Trash2, Edit2, Zap, Info, X } from 'lucide-react'
23
- import type { WorkflowDefinitionTrigger } from '../data/entities'
24
-
25
- interface DefinitionTriggersEditorProps {
26
- value: WorkflowDefinitionTrigger[]
27
- onChange: (triggers: WorkflowDefinitionTrigger[]) => void
28
- className?: string
29
- }
30
-
31
- const FILTER_OPERATORS = [
32
- { value: 'eq', label: 'Equals' },
33
- { value: 'neq', label: 'Not Equals' },
34
- { value: 'gt', label: 'Greater Than' },
35
- { value: 'gte', label: 'Greater Than or Equal' },
36
- { value: 'lt', label: 'Less Than' },
37
- { value: 'lte', label: 'Less Than or Equal' },
38
- { value: 'contains', label: 'Contains' },
39
- { value: 'startsWith', label: 'Starts With' },
40
- { value: 'endsWith', label: 'Ends With' },
41
- { value: 'in', label: 'In (array)' },
42
- { value: 'notIn', label: 'Not In (array)' },
43
- { value: 'exists', label: 'Exists' },
44
- { value: 'notExists', label: 'Not Exists' },
45
- { value: 'regex', label: 'Regex Match' },
46
- ] as const
47
-
48
- type TriggerFormValues = {
49
- triggerId: string
50
- name: string
51
- description: string
52
- eventPattern: string
53
- enabled: boolean
54
- priority: number
55
- filterConditions: Array<{ field: string; operator: string; value: string }>
56
- contextMappings: Array<{ targetKey: string; sourceExpression: string; defaultValue: string }>
57
- debounceMs: string
58
- maxConcurrentInstances: string
59
- }
60
-
61
- const defaultFormValues: TriggerFormValues = {
62
- triggerId: '',
63
- name: '',
64
- description: '',
65
- eventPattern: '',
66
- enabled: true,
67
- priority: 0,
68
- filterConditions: [],
69
- contextMappings: [],
70
- debounceMs: '',
71
- maxConcurrentInstances: '',
72
- }
73
-
74
- /**
75
- * DefinitionTriggersEditor
76
- *
77
- * Manages event triggers embedded in workflow definitions.
78
- * Works with local state - no API calls, changes are saved with the definition.
79
- */
80
- export function DefinitionTriggersEditor({
81
- value,
82
- onChange,
83
- className,
84
- }: DefinitionTriggersEditorProps) {
85
- const t = useT()
86
- const [showDialog, setShowDialog] = useState(false)
87
- const [editingTrigger, setEditingTrigger] = useState<WorkflowDefinitionTrigger | null>(null)
88
- const [formValues, setFormValues] = useState<TriggerFormValues>(defaultFormValues)
89
- const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null)
90
-
91
- // Generate trigger ID from name
92
- const generateTriggerId = useCallback((name: string) => {
93
- return name
94
- .toLowerCase()
95
- .replace(/[^a-z0-9]+/g, '_')
96
- .replace(/^_+|_+$/g, '')
97
- .substring(0, 50) || `trigger_${Date.now()}`
98
- }, [])
99
-
100
- // Parse condition value (try JSON, fallback to string)
101
- const parseConditionValue = (valueStr: string): unknown => {
102
- try {
103
- return JSON.parse(valueStr)
104
- } catch {
105
- return valueStr
106
- }
107
- }
108
-
109
- // Build trigger from form values
110
- const buildTriggerFromForm = useCallback((values: TriggerFormValues): WorkflowDefinitionTrigger => {
111
- const config: WorkflowDefinitionTrigger['config'] = {}
112
-
113
- if (values.filterConditions.length > 0) {
114
- config.filterConditions = values.filterConditions.map(fc => ({
115
- field: fc.field,
116
- operator: fc.operator as any,
117
- value: parseConditionValue(fc.value),
118
- }))
119
- }
120
-
121
- if (values.contextMappings.length > 0) {
122
- config.contextMapping = values.contextMappings.map(cm => ({
123
- targetKey: cm.targetKey,
124
- sourceExpression: cm.sourceExpression,
125
- defaultValue: cm.defaultValue ? parseConditionValue(cm.defaultValue) : undefined,
126
- }))
127
- }
128
-
129
- if (values.debounceMs) {
130
- config.debounceMs = parseInt(values.debounceMs, 10)
131
- }
132
-
133
- if (values.maxConcurrentInstances) {
134
- config.maxConcurrentInstances = parseInt(values.maxConcurrentInstances, 10)
135
- }
136
-
137
- return {
138
- triggerId: values.triggerId || generateTriggerId(values.name),
139
- name: values.name,
140
- description: values.description || null,
141
- eventPattern: values.eventPattern,
142
- enabled: values.enabled,
143
- priority: values.priority,
144
- config: Object.keys(config).length > 0 ? config : null,
145
- }
146
- }, [generateTriggerId])
147
-
148
- // Open dialog for creating new trigger
149
- const handleCreateNew = useCallback(() => {
150
- setEditingTrigger(null)
151
- setFormValues(defaultFormValues)
152
- setShowDialog(true)
153
- }, [])
154
-
155
- // Open dialog for editing trigger
156
- const handleEdit = useCallback((trigger: WorkflowDefinitionTrigger) => {
157
- setEditingTrigger(trigger)
158
- setFormValues({
159
- triggerId: trigger.triggerId,
160
- name: trigger.name,
161
- description: trigger.description || '',
162
- eventPattern: trigger.eventPattern,
163
- enabled: trigger.enabled,
164
- priority: trigger.priority,
165
- filterConditions: trigger.config?.filterConditions?.map(fc => ({
166
- field: fc.field,
167
- operator: fc.operator,
168
- value: typeof fc.value === 'string' ? fc.value : JSON.stringify(fc.value),
169
- })) || [],
170
- contextMappings: trigger.config?.contextMapping?.map(cm => ({
171
- targetKey: cm.targetKey,
172
- sourceExpression: cm.sourceExpression,
173
- defaultValue: cm.defaultValue !== undefined
174
- ? (typeof cm.defaultValue === 'string' ? cm.defaultValue : JSON.stringify(cm.defaultValue))
175
- : '',
176
- })) || [],
177
- debounceMs: trigger.config?.debounceMs?.toString() || '',
178
- maxConcurrentInstances: trigger.config?.maxConcurrentInstances?.toString() || '',
179
- })
180
- setShowDialog(true)
181
- }, [])
182
-
183
- // Close dialog
184
- const handleCloseDialog = useCallback(() => {
185
- setShowDialog(false)
186
- setEditingTrigger(null)
187
- setFormValues(defaultFormValues)
188
- }, [])
189
-
190
- // Submit form
191
- const handleSubmit = useCallback(() => {
192
- if (!formValues.name.trim()) {
193
- return
194
- }
195
- if (!formValues.eventPattern.trim()) {
196
- return
197
- }
198
-
199
- const newTrigger = buildTriggerFromForm(formValues)
200
-
201
- if (editingTrigger) {
202
- // Update existing trigger
203
- onChange(value.map(t => t.triggerId === editingTrigger.triggerId ? newTrigger : t))
204
- } else {
205
- // Check for duplicate triggerId
206
- const existingIds = new Set(value.map(t => t.triggerId))
207
- if (existingIds.has(newTrigger.triggerId)) {
208
- // Append timestamp to make unique
209
- newTrigger.triggerId = `${newTrigger.triggerId}_${Date.now()}`
210
- }
211
- // Add new trigger
212
- onChange([...value, newTrigger])
213
- }
214
-
215
- handleCloseDialog()
216
- }, [formValues, editingTrigger, buildTriggerFromForm, value, onChange, handleCloseDialog])
217
-
218
- // Delete trigger
219
- const handleDelete = useCallback((triggerId: string) => {
220
- onChange(value.filter(t => t.triggerId !== triggerId))
221
- setDeleteConfirmId(null)
222
- }, [value, onChange])
223
-
224
- // Add filter condition
225
- const addFilterCondition = useCallback(() => {
226
- setFormValues(prev => ({
227
- ...prev,
228
- filterConditions: [...prev.filterConditions, { field: '', operator: 'eq', value: '' }],
229
- }))
230
- }, [])
231
-
232
- // Remove filter condition
233
- const removeFilterCondition = useCallback((index: number) => {
234
- setFormValues(prev => ({
235
- ...prev,
236
- filterConditions: prev.filterConditions.filter((_, i) => i !== index),
237
- }))
238
- }, [])
239
-
240
- // Update filter condition
241
- const updateFilterCondition = useCallback((index: number, field: string, fieldValue: string) => {
242
- setFormValues(prev => ({
243
- ...prev,
244
- filterConditions: prev.filterConditions.map((fc, i) =>
245
- i === index ? { ...fc, [field]: fieldValue } : fc
246
- ),
247
- }))
248
- }, [])
249
-
250
- // Add context mapping
251
- const addContextMapping = useCallback(() => {
252
- setFormValues(prev => ({
253
- ...prev,
254
- contextMappings: [...prev.contextMappings, { targetKey: '', sourceExpression: '', defaultValue: '' }],
255
- }))
256
- }, [])
257
-
258
- // Remove context mapping
259
- const removeContextMapping = useCallback((index: number) => {
260
- setFormValues(prev => ({
261
- ...prev,
262
- contextMappings: prev.contextMappings.filter((_, i) => i !== index),
263
- }))
264
- }, [])
265
-
266
- // Update context mapping
267
- const updateContextMapping = useCallback((index: number, field: string, fieldValue: string) => {
268
- setFormValues(prev => ({
269
- ...prev,
270
- contextMappings: prev.contextMappings.map((cm, i) =>
271
- i === index ? { ...cm, [field]: fieldValue } : cm
272
- ),
273
- }))
274
- }, [])
275
-
276
- return (
277
- <div className={className}>
278
- <div className="rounded-lg border bg-card p-4">
279
- <div className="flex items-center justify-between mb-4">
280
- <div className="flex items-center gap-2">
281
- <Zap className="w-5 h-5 text-amber-500" />
282
- <h3 className="text-sm font-semibold uppercase text-muted-foreground">
283
- {t('workflows.triggers.title', 'Event Triggers')}
284
- </h3>
285
- </div>
286
- <Button size="sm" variant="outline" onClick={handleCreateNew}>
287
- <Plus className="w-4 h-4 mr-1" />
288
- {t('workflows.triggers.add', 'Add Trigger')}
289
- </Button>
290
- </div>
291
-
292
- <p className="text-xs text-muted-foreground mb-4">
293
- {t('workflows.triggers.description', 'Configure events that automatically start this workflow. When a matching event occurs in the system, a new workflow instance will be created with the mapped context.')}
294
- </p>
295
-
296
- {value.length === 0 ? (
297
- <Alert variant="info">
298
- <Info className="w-4 h-4" />
299
- <AlertTitle>{t('workflows.triggers.empty.title', 'No triggers configured')}</AlertTitle>
300
- <AlertDescription>
301
- {t('workflows.triggers.empty.description', 'Click "Add Trigger" to create an event trigger that automatically starts this workflow.')}
302
- </AlertDescription>
303
- </Alert>
304
- ) : (
305
- <div className="space-y-2">
306
- {value.map(trigger => (
307
- <div
308
- key={trigger.triggerId}
309
- className="flex items-center justify-between p-3 rounded-lg border bg-background hover:bg-accent/50 transition-colors"
310
- >
311
- <div className="flex items-center gap-3">
312
- <Badge variant={trigger.enabled ? 'default' : 'secondary'}>
313
- {trigger.enabled ? t('common.active', 'Active') : t('common.disabled', 'Disabled')}
314
- </Badge>
315
- <div>
316
- <div className="font-medium text-sm">{trigger.name}</div>
317
- <code className="text-xs text-muted-foreground">{trigger.eventPattern}</code>
318
- </div>
319
- </div>
320
- <div className="flex items-center gap-2">
321
- <Button size="sm" variant="ghost" onClick={() => handleEdit(trigger)}>
322
- <Edit2 className="w-4 h-4" />
323
- </Button>
324
- <Button
325
- size="sm"
326
- variant="ghost"
327
- className="text-destructive hover:text-destructive"
328
- onClick={() => setDeleteConfirmId(trigger.triggerId)}
329
- >
330
- <Trash2 className="w-4 h-4" />
331
- </Button>
332
- </div>
333
- </div>
334
- ))}
335
- </div>
336
- )}
337
- </div>
338
-
339
- {/* Create/Edit Dialog */}
340
- <Dialog open={showDialog} onOpenChange={setShowDialog}>
341
- <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
342
- <DialogHeader>
343
- <DialogTitle>
344
- {editingTrigger
345
- ? t('workflows.triggers.dialog.edit.title', 'Edit Event Trigger')
346
- : t('workflows.triggers.dialog.create.title', 'Create Event Trigger')
347
- }
348
- </DialogTitle>
349
- <DialogDescription>
350
- {t('workflows.triggers.dialog.description', 'Configure when this workflow should be automatically started based on system events.')}
351
- </DialogDescription>
352
- </DialogHeader>
353
-
354
- <div className="space-y-4 py-4">
355
- {/* Basic Info */}
356
- <div className="grid grid-cols-2 gap-4">
357
- <div className="space-y-1">
358
- <Label htmlFor="trigger-name">{t('workflows.triggers.fields.name', 'Name')} *</Label>
359
- <Input
360
- id="trigger-name"
361
- value={formValues.name}
362
- onChange={e => setFormValues(prev => ({ ...prev, name: e.target.value }))}
363
- placeholder={t('workflows.triggers.placeholders.name', 'Order Created Trigger')}
364
- />
365
- </div>
366
- <div className="space-y-1">
367
- <Label htmlFor="trigger-priority">{t('workflows.triggers.fields.priority', 'Priority')}</Label>
368
- <Input
369
- id="trigger-priority"
370
- type="number"
371
- value={formValues.priority}
372
- onChange={e => setFormValues(prev => ({ ...prev, priority: parseInt(e.target.value) || 0 }))}
373
- placeholder="0"
374
- />
375
- <p className="text-xs text-muted-foreground">
376
- {t('workflows.triggers.hints.priority', 'Higher priority triggers execute first')}
377
- </p>
378
- </div>
379
- </div>
380
-
381
- <div className="space-y-1">
382
- <Label htmlFor="trigger-description">{t('workflows.triggers.fields.description', 'Description')}</Label>
383
- <Textarea
384
- id="trigger-description"
385
- value={formValues.description}
386
- onChange={e => setFormValues(prev => ({ ...prev, description: e.target.value }))}
387
- placeholder={t('workflows.triggers.placeholders.description', 'Describe when this trigger should fire...')}
388
- rows={2}
389
- />
390
- </div>
391
-
392
- {/* Event Pattern */}
393
- <div className="space-y-1">
394
- <Label htmlFor="trigger-pattern">{t('workflows.triggers.fields.eventPattern', 'Event Pattern')} *</Label>
395
- <div className="flex gap-2">
396
- <Input
397
- id="trigger-pattern"
398
- value={formValues.eventPattern}
399
- onChange={e => setFormValues(prev => ({ ...prev, eventPattern: e.target.value }))}
400
- placeholder="sales.orders.created"
401
- className="flex-1"
402
- />
403
- <EventSelect
404
- value=""
405
- onChange={(eventId) => setFormValues(prev => ({ ...prev, eventPattern: eventId }))}
406
- placeholder={t('workflows.triggers.placeholders.quickSelect', 'Quick select...')}
407
- className="w-[200px]"
408
- />
409
- </div>
410
- <p className="text-xs text-muted-foreground">
411
- {t('workflows.triggers.hints.eventPattern', 'Use * as wildcard: "sales.orders.*" matches any order event')}
412
- </p>
413
- </div>
414
-
415
- {/* Enabled Switch */}
416
- <div className="flex items-center gap-2">
417
- <Switch
418
- id="trigger-enabled"
419
- checked={formValues.enabled}
420
- onCheckedChange={checked => setFormValues(prev => ({ ...prev, enabled: checked }))}
421
- />
422
- <Label htmlFor="trigger-enabled">{t('workflows.triggers.fields.enabled', 'Enabled')}</Label>
423
- </div>
424
-
425
- {/* Filter Conditions */}
426
- <div className="space-y-2">
427
- <div className="flex items-center justify-between">
428
- <Label>{t('workflows.triggers.fields.filterConditions', 'Filter Conditions')}</Label>
429
- <Button size="sm" variant="ghost" onClick={addFilterCondition}>
430
- <Plus className="w-4 h-4 mr-1" />
431
- {t('workflows.triggers.addCondition', 'Add Condition')}
432
- </Button>
433
- </div>
434
- <p className="text-xs text-muted-foreground">
435
- {t('workflows.triggers.hints.filterConditions', 'Only trigger when the event payload matches these conditions (all must match)')}
436
- </p>
437
- {formValues.filterConditions.map((fc, index) => (
438
- <div key={index} className="flex items-center gap-2">
439
- <Input
440
- value={fc.field}
441
- onChange={e => updateFilterCondition(index, 'field', e.target.value)}
442
- placeholder="status"
443
- className="w-1/3"
444
- />
445
- <select
446
- value={fc.operator}
447
- onChange={(e: React.ChangeEvent<HTMLSelectElement>) => updateFilterCondition(index, 'operator', e.target.value)}
448
- 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"
449
- >
450
- {FILTER_OPERATORS.map(op => (
451
- <option key={op.value} value={op.value}>
452
- {op.label}
453
- </option>
454
- ))}
455
- </select>
456
- <Input
457
- value={fc.value}
458
- onChange={e => updateFilterCondition(index, 'value', e.target.value)}
459
- placeholder="submitted"
460
- className="flex-1"
461
- />
462
- <Button size="icon" variant="ghost" onClick={() => removeFilterCondition(index)}>
463
- <X className="w-4 h-4" />
464
- </Button>
465
- </div>
466
- ))}
467
- </div>
468
-
469
- {/* Context Mapping */}
470
- <div className="space-y-2">
471
- <div className="flex items-center justify-between">
472
- <Label>{t('workflows.triggers.fields.contextMapping', 'Context Mapping')}</Label>
473
- <Button size="sm" variant="ghost" onClick={addContextMapping}>
474
- <Plus className="w-4 h-4 mr-1" />
475
- {t('workflows.triggers.addMapping', 'Add Mapping')}
476
- </Button>
477
- </div>
478
- <p className="text-xs text-muted-foreground">
479
- {t('workflows.triggers.hints.contextMapping', "Map values from the event payload to the workflow's initial context")}
480
- </p>
481
- {formValues.contextMappings.map((cm, index) => (
482
- <div key={index} className="flex items-center gap-2">
483
- <Input
484
- value={cm.targetKey}
485
- onChange={e => updateContextMapping(index, 'targetKey', e.target.value)}
486
- placeholder="orderId"
487
- className="w-1/3"
488
- />
489
- <span className="text-muted-foreground">=</span>
490
- <Input
491
- value={cm.sourceExpression}
492
- onChange={e => updateContextMapping(index, 'sourceExpression', e.target.value)}
493
- placeholder="id"
494
- className="flex-1"
495
- />
496
- <Input
497
- value={cm.defaultValue}
498
- onChange={e => updateContextMapping(index, 'defaultValue', e.target.value)}
499
- placeholder="default"
500
- className="w-24"
501
- />
502
- <Button size="icon" variant="ghost" onClick={() => removeContextMapping(index)}>
503
- <X className="w-4 h-4" />
504
- </Button>
505
- </div>
506
- ))}
507
- </div>
508
-
509
- {/* Advanced Options */}
510
- <div className="grid grid-cols-2 gap-4">
511
- <div className="space-y-1">
512
- <Label htmlFor="trigger-debounce">{t('workflows.triggers.fields.debounceMs', 'Debounce (ms)')}</Label>
513
- <Input
514
- id="trigger-debounce"
515
- type="number"
516
- value={formValues.debounceMs}
517
- onChange={e => setFormValues(prev => ({ ...prev, debounceMs: e.target.value }))}
518
- placeholder="0"
519
- />
520
- <p className="text-xs text-muted-foreground">
521
- {t('workflows.triggers.hints.debounce', 'Delay to prevent rapid re-triggers')}
522
- </p>
523
- </div>
524
- <div className="space-y-1">
525
- <Label htmlFor="trigger-max-concurrent">{t('workflows.triggers.fields.maxConcurrent', 'Max Concurrent Instances')}</Label>
526
- <Input
527
- id="trigger-max-concurrent"
528
- type="number"
529
- value={formValues.maxConcurrentInstances}
530
- onChange={e => setFormValues(prev => ({ ...prev, maxConcurrentInstances: e.target.value }))}
531
- placeholder={t('workflows.triggers.placeholders.unlimited', 'Unlimited')}
532
- />
533
- <p className="text-xs text-muted-foreground">
534
- {t('workflows.triggers.hints.maxConcurrent', 'Limit simultaneous workflow instances')}
535
- </p>
536
- </div>
537
- </div>
538
- </div>
539
-
540
- <DialogFooter>
541
- <Button variant="outline" onClick={handleCloseDialog}>
542
- {t('common.cancel', 'Cancel')}
543
- </Button>
544
- <Button
545
- onClick={handleSubmit}
546
- disabled={!formValues.name.trim() || !formValues.eventPattern.trim()}
547
- >
548
- {editingTrigger
549
- ? t('common.update', 'Update')
550
- : t('common.create', 'Create')
551
- }
552
- </Button>
553
- </DialogFooter>
554
- </DialogContent>
555
- </Dialog>
556
-
557
- {/* Delete Confirmation Dialog */}
558
- <Dialog open={!!deleteConfirmId} onOpenChange={() => setDeleteConfirmId(null)}>
559
- <DialogContent>
560
- <DialogHeader>
561
- <DialogTitle>{t('workflows.triggers.delete.title', 'Delete Event Trigger?')}</DialogTitle>
562
- <DialogDescription>
563
- {t('workflows.triggers.delete.description', 'This will remove the event trigger. The change will take effect when you save the workflow definition.')}
564
- </DialogDescription>
565
- </DialogHeader>
566
- <DialogFooter>
567
- <Button variant="outline" onClick={() => setDeleteConfirmId(null)}>
568
- {t('common.cancel', 'Cancel')}
569
- </Button>
570
- <Button
571
- variant="destructive"
572
- onClick={() => deleteConfirmId && handleDelete(deleteConfirmId)}
573
- >
574
- {t('common.delete', 'Delete')}
575
- </Button>
576
- </DialogFooter>
577
- </DialogContent>
578
- </Dialog>
579
- </div>
580
- )
581
- }