@startsimpli/funnels 0.4.6 → 0.4.8

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 (46) hide show
  1. package/package.json +1 -1
  2. package/src/api/client.test.ts +76 -76
  3. package/src/api/client.ts +16 -16
  4. package/src/components/FilterRuleEditor/FilterRuleEditor.stories.tsx +24 -24
  5. package/src/components/FilterRuleEditor/FilterRuleEditor.test.tsx +39 -39
  6. package/src/components/FilterRuleEditor/FilterRuleEditor.tsx +1 -1
  7. package/src/components/FilterRuleEditor/RuleRow.tsx +8 -8
  8. package/src/components/FunnelCard/FunnelCard.stories.tsx +118 -118
  9. package/src/components/FunnelCard/FunnelCard.test.ts +48 -48
  10. package/src/components/FunnelCard/FunnelCard.test.tsx +40 -40
  11. package/src/components/FunnelCard/FunnelCard.tsx +3 -3
  12. package/src/components/FunnelPreview/FunnelPreview.test.tsx +63 -63
  13. package/src/components/FunnelPreview/FunnelPreview.tsx +11 -11
  14. package/src/components/FunnelPreview/StageBreakdown.tsx +5 -5
  15. package/src/components/FunnelPreview/example.tsx +43 -43
  16. package/src/components/FunnelRunHistory/FunnelRunHistory.stories.tsx +70 -70
  17. package/src/components/FunnelRunHistory/FunnelRunHistory.test.tsx +63 -63
  18. package/src/components/FunnelRunHistory/FunnelRunHistory.tsx +12 -12
  19. package/src/components/FunnelRunHistory/RunDetailsModal.tsx +5 -5
  20. package/src/components/FunnelRunHistory/RunFilters.tsx +8 -8
  21. package/src/components/FunnelRunHistory/RunRow.tsx +7 -7
  22. package/src/components/FunnelRunHistory/StageBreakdownList.tsx +9 -9
  23. package/src/components/FunnelRunHistory/types.ts +6 -6
  24. package/src/components/FunnelStageBuilder/FunnelStageBuilder.stories.tsx +41 -41
  25. package/src/components/FunnelStageBuilder/FunnelStageBuilder.test.tsx +6 -6
  26. package/src/components/FunnelStageBuilder/FunnelStageBuilder.tsx +5 -5
  27. package/src/components/FunnelStageBuilder/StageActions.test.tsx +7 -7
  28. package/src/components/FunnelStageBuilder/StageActions.tsx +4 -4
  29. package/src/components/FunnelStageBuilder/StageCard.tsx +3 -3
  30. package/src/components/FunnelStageBuilder/StageForm.tsx +14 -14
  31. package/src/components/FunnelVisualFlow/FunnelVisualFlow.stories.tsx +42 -42
  32. package/src/components/FunnelVisualFlow/FunnelVisualFlow.test.tsx +31 -31
  33. package/src/components/FunnelVisualFlow/FunnelVisualFlow.tsx +4 -4
  34. package/src/components/FunnelVisualFlow/StageNode.tsx +8 -8
  35. package/src/components/FunnelVisualFlow/example.tsx +7 -7
  36. package/src/core/engine.test.ts +271 -271
  37. package/src/core/engine.ts +60 -60
  38. package/src/core/evaluator.example.ts +31 -31
  39. package/src/core/evaluator.test.ts +92 -92
  40. package/src/core/evaluator.ts +23 -23
  41. package/src/core/field-resolver.example.ts +1 -1
  42. package/src/core/field-resolver.test.ts +4 -4
  43. package/src/store/create-funnel-store.test.ts +32 -32
  44. package/src/store/create-funnel-store.ts +3 -3
  45. package/src/store/types.ts +2 -2
  46. package/src/types/index.ts +64 -64
@@ -47,7 +47,7 @@ const investorFieldRegistry: FieldDefinition[] = [
47
47
  category: 'Investment',
48
48
  operators: ['eq', 'ne', 'gt', 'lt', 'gte', 'lte'],
49
49
  constraints: {
50
- min_value: 0,
50
+ minValue: 0,
51
51
  },
52
52
  },
53
53
  {
@@ -57,8 +57,8 @@ const investorFieldRegistry: FieldDefinition[] = [
57
57
  category: 'Metrics',
58
58
  operators: ['eq', 'ne', 'gt', 'lt', 'gte', 'lte'],
59
59
  constraints: {
60
- min_value: 0,
61
- max_value: 100,
60
+ minValue: 0,
61
+ maxValue: 100,
62
62
  },
63
63
  },
64
64
  {
@@ -108,7 +108,7 @@ const recipeFieldRegistry: FieldDefinition[] = [
108
108
  category: 'Time',
109
109
  operators: ['eq', 'ne', 'gt', 'lt', 'gte', 'lte'],
110
110
  constraints: {
111
- min_value: 0,
111
+ minValue: 0,
112
112
  },
113
113
  },
114
114
  {
@@ -119,7 +119,7 @@ const recipeFieldRegistry: FieldDefinition[] = [
119
119
  operators: ['eq', 'is_true', 'is_false'],
120
120
  },
121
121
  {
122
- name: 'created_at',
122
+ name: 'createdAt',
123
123
  label: 'Created Date',
124
124
  type: 'date',
125
125
  category: 'Metadata',
@@ -132,20 +132,20 @@ const recipeFieldRegistry: FieldDefinition[] = [
132
132
  // ============================================================================
133
133
 
134
134
  describe('FilterRuleEditor - Generic Validation', () => {
135
- it('validates empty field_path', () => {
135
+ it('validates empty fieldPath', () => {
136
136
  const rule: FilterRule = {
137
- field_path: '',
137
+ fieldPath: '',
138
138
  operator: 'eq',
139
139
  value: 'test',
140
140
  };
141
141
 
142
142
  const errors = validateFilterRule(rule);
143
- expect(errors).toContain('field_path is required');
143
+ expect(errors).toContain('fieldPath is required');
144
144
  });
145
145
 
146
146
  it('validates missing operator', () => {
147
147
  const rule: FilterRule = {
148
- field_path: 'name',
148
+ fieldPath: 'name',
149
149
  operator: '' as any,
150
150
  value: 'test',
151
151
  };
@@ -156,7 +156,7 @@ describe('FilterRuleEditor - Generic Validation', () => {
156
156
 
157
157
  it('validates missing value for operators that need it', () => {
158
158
  const rule: FilterRule = {
159
- field_path: 'name',
159
+ fieldPath: 'name',
160
160
  operator: 'eq',
161
161
  value: undefined,
162
162
  };
@@ -171,7 +171,7 @@ describe('FilterRuleEditor - Generic Validation', () => {
171
171
 
172
172
  nullOps.forEach((op) => {
173
173
  const rule: FilterRule = {
174
- field_path: 'email',
174
+ fieldPath: 'email',
175
175
  operator: op as any,
176
176
  value: null,
177
177
  };
@@ -185,7 +185,7 @@ describe('FilterRuleEditor - Generic Validation', () => {
185
185
  describe('FilterRuleEditor - Investor Domain', () => {
186
186
  it('creates string equality rule', () => {
187
187
  const rule: FilterRule = {
188
- field_path: 'name',
188
+ fieldPath: 'name',
189
189
  operator: 'eq',
190
190
  value: 'John Doe',
191
191
  };
@@ -195,7 +195,7 @@ describe('FilterRuleEditor - Investor Domain', () => {
195
195
 
196
196
  it('creates string contains rule', () => {
197
197
  const rule: FilterRule = {
198
- field_path: 'email',
198
+ fieldPath: 'email',
199
199
  operator: 'contains',
200
200
  value: '@vc.com',
201
201
  };
@@ -205,7 +205,7 @@ describe('FilterRuleEditor - Investor Domain', () => {
205
205
 
206
206
  it('creates choice equality rule', () => {
207
207
  const rule: FilterRule = {
208
- field_path: 'firm.stage',
208
+ fieldPath: 'firm.stage',
209
209
  operator: 'eq',
210
210
  value: 'Series A',
211
211
  };
@@ -215,7 +215,7 @@ describe('FilterRuleEditor - Investor Domain', () => {
215
215
 
216
216
  it('creates choice multi-select rule', () => {
217
217
  const rule: FilterRule = {
218
- field_path: 'firm.stage',
218
+ fieldPath: 'firm.stage',
219
219
  operator: 'in',
220
220
  value: ['Series A', 'Series B', 'Series C'],
221
221
  };
@@ -225,7 +225,7 @@ describe('FilterRuleEditor - Investor Domain', () => {
225
225
 
226
226
  it('creates number comparison rule', () => {
227
227
  const rule: FilterRule = {
228
- field_path: 'firm.check_size_min',
228
+ fieldPath: 'firm.check_size_min',
229
229
  operator: 'gte',
230
230
  value: 1000000,
231
231
  };
@@ -235,7 +235,7 @@ describe('FilterRuleEditor - Investor Domain', () => {
235
235
 
236
236
  it('creates number range rule', () => {
237
237
  const rule: FilterRule = {
238
- field_path: 'metrics.icp_score',
238
+ fieldPath: 'metrics.icp_score',
239
239
  operator: 'gte',
240
240
  value: 80,
241
241
  };
@@ -245,7 +245,7 @@ describe('FilterRuleEditor - Investor Domain', () => {
245
245
 
246
246
  it('creates tag rule', () => {
247
247
  const rule: FilterRule = {
248
- field_path: 'tags',
248
+ fieldPath: 'tags',
249
249
  operator: 'has_tag',
250
250
  value: 'priority',
251
251
  };
@@ -257,7 +257,7 @@ describe('FilterRuleEditor - Investor Domain', () => {
257
257
  describe('FilterRuleEditor - Recipe Domain', () => {
258
258
  it('creates recipe title search rule', () => {
259
259
  const rule: FilterRule = {
260
- field_path: 'title',
260
+ fieldPath: 'title',
261
261
  operator: 'contains',
262
262
  value: 'pasta',
263
263
  };
@@ -267,7 +267,7 @@ describe('FilterRuleEditor - Recipe Domain', () => {
267
267
 
268
268
  it('creates cuisine filter rule', () => {
269
269
  const rule: FilterRule = {
270
- field_path: 'cuisine',
270
+ fieldPath: 'cuisine',
271
271
  operator: 'eq',
272
272
  value: 'Italian',
273
273
  };
@@ -277,7 +277,7 @@ describe('FilterRuleEditor - Recipe Domain', () => {
277
277
 
278
278
  it('creates multiple cuisine rule', () => {
279
279
  const rule: FilterRule = {
280
- field_path: 'cuisine',
280
+ fieldPath: 'cuisine',
281
281
  operator: 'in',
282
282
  value: ['Italian', 'French', 'Japanese'],
283
283
  };
@@ -287,7 +287,7 @@ describe('FilterRuleEditor - Recipe Domain', () => {
287
287
 
288
288
  it('creates difficulty filter rule', () => {
289
289
  const rule: FilterRule = {
290
- field_path: 'difficulty',
290
+ fieldPath: 'difficulty',
291
291
  operator: 'eq',
292
292
  value: 'Easy',
293
293
  };
@@ -297,7 +297,7 @@ describe('FilterRuleEditor - Recipe Domain', () => {
297
297
 
298
298
  it('creates prep time rule', () => {
299
299
  const rule: FilterRule = {
300
- field_path: 'prep_time_minutes',
300
+ fieldPath: 'prep_time_minutes',
301
301
  operator: 'lte',
302
302
  value: 30,
303
303
  };
@@ -307,7 +307,7 @@ describe('FilterRuleEditor - Recipe Domain', () => {
307
307
 
308
308
  it('creates boolean rule', () => {
309
309
  const rule: FilterRule = {
310
- field_path: 'is_vegetarian',
310
+ fieldPath: 'is_vegetarian',
311
311
  operator: 'is_true',
312
312
  value: null,
313
313
  };
@@ -317,7 +317,7 @@ describe('FilterRuleEditor - Recipe Domain', () => {
317
317
 
318
318
  it('creates date comparison rule', () => {
319
319
  const rule: FilterRule = {
320
- field_path: 'created_at',
320
+ fieldPath: 'createdAt',
321
321
  operator: 'gte',
322
322
  value: '2024-01-01',
323
323
  };
@@ -330,17 +330,17 @@ describe('FilterRuleEditor - Complex Multi-Rule Scenarios', () => {
330
330
  it('validates AND logic with multiple rules', () => {
331
331
  const rules: FilterRule[] = [
332
332
  {
333
- field_path: 'firm.stage',
333
+ fieldPath: 'firm.stage',
334
334
  operator: 'in',
335
335
  value: ['Series A', 'Series B'],
336
336
  },
337
337
  {
338
- field_path: 'firm.check_size_min',
338
+ fieldPath: 'firm.check_size_min',
339
339
  operator: 'gte',
340
340
  value: 500000,
341
341
  },
342
342
  {
343
- field_path: 'metrics.icp_score',
343
+ fieldPath: 'metrics.icp_score',
344
344
  operator: 'gte',
345
345
  value: 70,
346
346
  },
@@ -354,17 +354,17 @@ describe('FilterRuleEditor - Complex Multi-Rule Scenarios', () => {
354
354
  it('validates OR logic with different field types', () => {
355
355
  const rules: FilterRule[] = [
356
356
  {
357
- field_path: 'name',
357
+ fieldPath: 'name',
358
358
  operator: 'contains',
359
359
  value: 'Acme',
360
360
  },
361
361
  {
362
- field_path: 'tags',
362
+ fieldPath: 'tags',
363
363
  operator: 'has_tag',
364
364
  value: 'hot-lead',
365
365
  },
366
366
  {
367
- field_path: 'metrics.icp_score',
367
+ fieldPath: 'metrics.icp_score',
368
368
  operator: 'gte',
369
369
  value: 90,
370
370
  },
@@ -378,22 +378,22 @@ describe('FilterRuleEditor - Complex Multi-Rule Scenarios', () => {
378
378
  it('validates recipe filter with multiple conditions', () => {
379
379
  const rules: FilterRule[] = [
380
380
  {
381
- field_path: 'cuisine',
381
+ fieldPath: 'cuisine',
382
382
  operator: 'in',
383
383
  value: ['Italian', 'French'],
384
384
  },
385
385
  {
386
- field_path: 'difficulty',
386
+ fieldPath: 'difficulty',
387
387
  operator: 'eq',
388
388
  value: 'Easy',
389
389
  },
390
390
  {
391
- field_path: 'prep_time_minutes',
391
+ fieldPath: 'prep_time_minutes',
392
392
  operator: 'lte',
393
393
  value: 30,
394
394
  },
395
395
  {
396
- field_path: 'is_vegetarian',
396
+ fieldPath: 'is_vegetarian',
397
397
  operator: 'is_true',
398
398
  value: null,
399
399
  },
@@ -477,7 +477,7 @@ describe('FilterRuleEditor - Operator Filtering', () => {
477
477
  });
478
478
 
479
479
  it('provides correct operators for date fields', () => {
480
- const dateField = recipeFieldRegistry.find((f) => f.name === 'created_at');
480
+ const dateField = recipeFieldRegistry.find((f) => f.name === 'createdAt');
481
481
  expect(dateField?.operators).toContain('eq');
482
482
  expect(dateField?.operators).toContain('gt');
483
483
  expect(dateField?.operators).toContain('lte');
@@ -507,14 +507,14 @@ describe('FilterRuleEditor - Value Constraints', () => {
507
507
  const scoreField = investorFieldRegistry.find(
508
508
  (f) => f.name === 'metrics.icp_score'
509
509
  );
510
- expect(scoreField?.constraints?.min_value).toBe(0);
511
- expect(scoreField?.constraints?.max_value).toBe(100);
510
+ expect(scoreField?.constraints?.minValue).toBe(0);
511
+ expect(scoreField?.constraints?.maxValue).toBe(100);
512
512
  });
513
513
 
514
514
  it('validates number constraints for prep time', () => {
515
515
  const prepField = recipeFieldRegistry.find(
516
516
  (f) => f.name === 'prep_time_minutes'
517
517
  );
518
- expect(prepField?.constraints?.min_value).toBe(0);
518
+ expect(prepField?.constraints?.minValue).toBe(0);
519
519
  });
520
520
  });
@@ -86,7 +86,7 @@ export function FilterRuleEditor({
86
86
  // Add a new empty rule
87
87
  const handleAddRule = () => {
88
88
  const newRule: FilterRule = {
89
- field_path: '',
89
+ fieldPath: '',
90
90
  operator: 'eq' as any,
91
91
  value: null,
92
92
  };
@@ -67,7 +67,7 @@ export function RuleRow({
67
67
  className = '',
68
68
  }: RuleRowProps) {
69
69
  // Find the selected field definition
70
- const selectedField = fieldRegistry.find((f) => f.name === rule.field_path);
70
+ const selectedField = fieldRegistry.find((f) => f.name === rule.fieldPath);
71
71
 
72
72
  // Get valid operators for the selected field
73
73
  const availableOperators = selectedField?.operators || [];
@@ -82,7 +82,7 @@ export function RuleRow({
82
82
  const field = fieldRegistry.find((f) => f.name === fieldName);
83
83
  onChange({
84
84
  ...rule,
85
- field_path: fieldName,
85
+ fieldPath: fieldName,
86
86
  operator: field?.operators[0] || ('' as Operator),
87
87
  value: null,
88
88
  });
@@ -157,8 +157,8 @@ export function RuleRow({
157
157
  <NumberValueInput
158
158
  value={rule.value}
159
159
  onChange={handleValueChange}
160
- min={constraints?.min_value as number}
161
- max={constraints?.max_value as number}
160
+ min={constraints?.minValue as number}
161
+ max={constraints?.maxValue as number}
162
162
  />
163
163
  );
164
164
 
@@ -167,8 +167,8 @@ export function RuleRow({
167
167
  <DateValueInput
168
168
  value={rule.value || null}
169
169
  onChange={handleValueChange}
170
- min={constraints?.min_value as string}
171
- max={constraints?.max_value as string}
170
+ min={constraints?.minValue as string}
171
+ max={constraints?.maxValue as string}
172
172
  />
173
173
  );
174
174
 
@@ -228,7 +228,7 @@ export function RuleRow({
228
228
  <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 pr-8">
229
229
  <FieldSelector
230
230
  fields={fieldRegistry}
231
- value={rule.field_path}
231
+ value={rule.fieldPath}
232
232
  onChange={handleFieldChange}
233
233
  />
234
234
 
@@ -236,7 +236,7 @@ export function RuleRow({
236
236
  operators={availableOperators}
237
237
  value={rule.operator || ''}
238
238
  onChange={handleOperatorChange}
239
- disabled={!rule.field_path}
239
+ disabled={!rule.fieldPath}
240
240
  />
241
241
 
242
242
  {needsValue && renderValueInput()}