feat: Add multi-branch workflow conditions (switch/case)
Add a `mode: 'multi'` variant to CONDITION steps that allows matching a field against multiple values, each routing to its own named branch. Branches are evaluated in order; first match wins. A `default` branch catches unmatched contacts. - Schema: z.union for legacy binary + multi-branch condition configs - Backend: executeCondition handles multi-branch evaluation with stable branch IDs, falls back to default when no branch matches - Builder: Dynamic branch rendering with per-branch colors/labels, dagre layout spreads branches horizontally - Config UI: Mode toggle (Simple If/Else vs Multi-branch Switch), dynamic branch list with add/remove - Tests: 8 new multi-branch tests covering matching, ordering, default fallback, mixed operators, and transition routing - Full backward compatibility: all changes gated on config.mode === 'multi' Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -260,22 +260,52 @@ export const WorkflowStepConfigSchemas = {
|
||||
eventName: z.string().min(1),
|
||||
timeout: z.number().positive().max(31536000, 'Timeout cannot exceed 365 days (31,536,000 seconds)').optional(),
|
||||
}),
|
||||
condition: z.object({
|
||||
field: z.string().min(1),
|
||||
operator: z.enum([
|
||||
'equals',
|
||||
'notEquals',
|
||||
'contains',
|
||||
'notContains',
|
||||
'greaterThan',
|
||||
'lessThan',
|
||||
'greaterThanOrEqual',
|
||||
'lessThanOrEqual',
|
||||
'exists',
|
||||
'notExists',
|
||||
]),
|
||||
value: z.any().optional(),
|
||||
}),
|
||||
condition: z.union([
|
||||
// Legacy binary condition (if/else)
|
||||
z.object({
|
||||
field: z.string().min(1),
|
||||
operator: z.enum([
|
||||
'equals',
|
||||
'notEquals',
|
||||
'contains',
|
||||
'notContains',
|
||||
'greaterThan',
|
||||
'lessThan',
|
||||
'greaterThanOrEqual',
|
||||
'lessThanOrEqual',
|
||||
'exists',
|
||||
'notExists',
|
||||
]),
|
||||
value: z.any().optional(),
|
||||
}),
|
||||
// Multi-branch condition (switch/case)
|
||||
z.object({
|
||||
mode: z.literal('multi'),
|
||||
field: z.string().min(1),
|
||||
branches: z
|
||||
.array(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
operator: z.enum([
|
||||
'equals',
|
||||
'notEquals',
|
||||
'contains',
|
||||
'notContains',
|
||||
'greaterThan',
|
||||
'lessThan',
|
||||
'greaterThanOrEqual',
|
||||
'lessThanOrEqual',
|
||||
'exists',
|
||||
'notExists',
|
||||
]),
|
||||
value: z.any().optional(),
|
||||
}),
|
||||
)
|
||||
.min(1)
|
||||
.max(20),
|
||||
}),
|
||||
]),
|
||||
webhook: z.object({
|
||||
url: z.string().url(),
|
||||
method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).default('POST'),
|
||||
|
||||
Reference in New Issue
Block a user