@startsimpli/funnels 0.4.6 → 0.4.7

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
@@ -26,17 +26,17 @@ const createMockApiClient = (runs: FunnelRun[]): FunnelApiClient => {
26
26
  // Sample runs
27
27
  const completedRun: FunnelRun = {
28
28
  id: 'run-1',
29
- funnel_id: 'funnel-1',
29
+ funnelId: 'funnel-1',
30
30
  status: 'completed',
31
- trigger_type: 'manual',
32
- started_at: '2024-02-10T14:30:00Z',
33
- completed_at: '2024-02-10T14:35:00Z',
34
- duration_ms: 300000,
35
- total_input: 1000,
36
- total_matched: 235,
37
- total_excluded: 765,
38
- total_tagged: 235,
39
- stage_stats: {
31
+ triggerType: 'manual',
32
+ startedAt: '2024-02-10T14:30:00Z',
33
+ completedAt: '2024-02-10T14:35:00Z',
34
+ durationMs: 300000,
35
+ totalInput: 1000,
36
+ totalMatched: 235,
37
+ totalExcluded: 765,
38
+ totalTagged: 235,
39
+ stageStats: {
40
40
  'stage-1': { input: 1000, matched: 450, excluded: 550 },
41
41
  'stage-2': { input: 450, matched: 300, excluded: 150 },
42
42
  'stage-3': { input: 300, matched: 235, excluded: 65 },
@@ -45,46 +45,46 @@ const completedRun: FunnelRun = {
45
45
 
46
46
  const runningRun: FunnelRun = {
47
47
  id: 'run-2',
48
- funnel_id: 'funnel-1',
48
+ funnelId: 'funnel-1',
49
49
  status: 'running',
50
- trigger_type: 'scheduled',
51
- started_at: new Date().toISOString(),
52
- total_input: 1000,
53
- total_matched: 0,
54
- total_excluded: 0,
55
- total_tagged: 0,
56
- stage_stats: {},
50
+ triggerType: 'scheduled',
51
+ startedAt: new Date().toISOString(),
52
+ totalInput: 1000,
53
+ totalMatched: 0,
54
+ totalExcluded: 0,
55
+ totalTagged: 0,
56
+ stageStats: {},
57
57
  };
58
58
 
59
59
  const failedRun: FunnelRun = {
60
60
  id: 'run-3',
61
- funnel_id: 'funnel-1',
61
+ funnelId: 'funnel-1',
62
62
  status: 'failed',
63
- trigger_type: 'manual',
64
- started_at: '2024-02-10T12:00:00Z',
65
- completed_at: '2024-02-10T12:01:30Z',
66
- duration_ms: 90000,
67
- total_input: 1000,
68
- total_matched: 0,
69
- total_excluded: 0,
70
- total_tagged: 0,
63
+ triggerType: 'manual',
64
+ startedAt: '2024-02-10T12:00:00Z',
65
+ completedAt: '2024-02-10T12:01:30Z',
66
+ durationMs: 90000,
67
+ totalInput: 1000,
68
+ totalMatched: 0,
69
+ totalExcluded: 0,
70
+ totalTagged: 0,
71
71
  error: 'Database connection timeout',
72
- stage_stats: {},
72
+ stageStats: {},
73
73
  };
74
74
 
75
75
  const cancelledRun: FunnelRun = {
76
76
  id: 'run-4',
77
- funnel_id: 'funnel-1',
77
+ funnelId: 'funnel-1',
78
78
  status: 'cancelled',
79
- trigger_type: 'api',
80
- started_at: '2024-02-10T11:00:00Z',
81
- completed_at: '2024-02-10T11:02:00Z',
82
- duration_ms: 120000,
83
- total_input: 500,
84
- total_matched: 50,
85
- total_excluded: 450,
86
- total_tagged: 50,
87
- stage_stats: {},
79
+ triggerType: 'api',
80
+ startedAt: '2024-02-10T11:00:00Z',
81
+ completedAt: '2024-02-10T11:02:00Z',
82
+ durationMs: 120000,
83
+ totalInput: 500,
84
+ totalMatched: 50,
85
+ totalExcluded: 450,
86
+ totalTagged: 50,
87
+ stageStats: {},
88
88
  };
89
89
 
90
90
  const meta: Meta<typeof FunnelRunHistory> = {
@@ -113,18 +113,18 @@ export const CompletedRuns: Story = {
113
113
  {
114
114
  ...completedRun,
115
115
  id: 'run-5',
116
- started_at: '2024-02-09T14:30:00Z',
117
- completed_at: '2024-02-09T14:32:00Z',
118
- duration_ms: 120000,
119
- total_matched: 180,
116
+ startedAt: '2024-02-09T14:30:00Z',
117
+ completedAt: '2024-02-09T14:32:00Z',
118
+ durationMs: 120000,
119
+ totalMatched: 180,
120
120
  },
121
121
  {
122
122
  ...completedRun,
123
123
  id: 'run-6',
124
- started_at: '2024-02-08T10:00:00Z',
125
- completed_at: '2024-02-08T10:05:00Z',
126
- duration_ms: 300000,
127
- total_matched: 300,
124
+ startedAt: '2024-02-08T10:00:00Z',
125
+ completedAt: '2024-02-08T10:05:00Z',
126
+ durationMs: 300000,
127
+ totalMatched: 300,
128
128
  },
129
129
  ]),
130
130
  },
@@ -142,8 +142,8 @@ export const MixedStatuses: Story = {
142
142
  {
143
143
  ...completedRun,
144
144
  id: 'run-7',
145
- started_at: '2024-02-09T14:30:00Z',
146
- completed_at: '2024-02-09T14:32:00Z',
145
+ startedAt: '2024-02-09T14:30:00Z',
146
+ completedAt: '2024-02-09T14:32:00Z',
147
147
  },
148
148
  ]),
149
149
  },
@@ -159,15 +159,15 @@ export const FailedRuns: Story = {
159
159
  ...failedRun,
160
160
  id: 'run-8',
161
161
  error: 'Timeout connecting to database',
162
- started_at: '2024-02-09T14:30:00Z',
163
- completed_at: '2024-02-09T14:31:00Z',
162
+ startedAt: '2024-02-09T14:30:00Z',
163
+ completedAt: '2024-02-09T14:31:00Z',
164
164
  },
165
165
  {
166
166
  ...failedRun,
167
167
  id: 'run-9',
168
168
  error: 'Invalid field reference: firm.invalid_field',
169
- started_at: '2024-02-08T10:00:00Z',
170
- completed_at: '2024-02-08T10:00:30Z',
169
+ startedAt: '2024-02-08T10:00:00Z',
170
+ completedAt: '2024-02-08T10:00:30Z',
171
171
  },
172
172
  ]),
173
173
  },
@@ -182,7 +182,7 @@ export const RunningRuns: Story = {
182
182
  {
183
183
  ...runningRun,
184
184
  id: 'run-10',
185
- started_at: new Date(Date.now() - 60000).toISOString(), // 1 minute ago
185
+ startedAt: new Date(Date.now() - 60000).toISOString(), // 1 minute ago
186
186
  },
187
187
  ]),
188
188
  },
@@ -204,10 +204,10 @@ export const LargeHistory: Story = {
204
204
  Array.from({ length: 50 }, (_, i) => ({
205
205
  ...completedRun,
206
206
  id: `run-${i}`,
207
- started_at: new Date(Date.now() - i * 86400000).toISOString(), // i days ago
208
- total_matched: 200 + Math.floor(Math.random() * 100),
209
- duration_ms: 120000 + Math.floor(Math.random() * 180000),
210
- trigger_type: i % 3 === 0 ? 'scheduled' : i % 3 === 1 ? 'manual' : 'api',
207
+ startedAt: new Date(Date.now() - i * 86400000).toISOString(), // i days ago
208
+ totalMatched: 200 + Math.floor(Math.random() * 100),
209
+ durationMs: 120000 + Math.floor(Math.random() * 180000),
210
+ triggerType: i % 3 === 0 ? 'scheduled' : i % 3 === 1 ? 'manual' : 'api',
211
211
  }))
212
212
  ),
213
213
  },
@@ -218,10 +218,10 @@ export const DifferentTriggers: Story = {
218
218
  args: {
219
219
  funnelId: 'funnel-1',
220
220
  apiClient: createMockApiClient([
221
- { ...completedRun, id: 'run-11', trigger_type: 'manual' },
222
- { ...completedRun, id: 'run-12', trigger_type: 'scheduled' },
223
- { ...completedRun, id: 'run-13', trigger_type: 'api' },
224
- { ...completedRun, id: 'run-14', trigger_type: 'webhook' },
221
+ { ...completedRun, id: 'run-11', triggerType: 'manual' },
222
+ { ...completedRun, id: 'run-12', triggerType: 'scheduled' },
223
+ { ...completedRun, id: 'run-13', triggerType: 'api' },
224
+ { ...completedRun, id: 'run-14', triggerType: 'webhook' },
225
225
  ]),
226
226
  },
227
227
  };
@@ -233,9 +233,9 @@ export const HighMatchRate: Story = {
233
233
  apiClient: createMockApiClient([
234
234
  {
235
235
  ...completedRun,
236
- total_input: 1000,
237
- total_matched: 950,
238
- total_excluded: 50,
236
+ totalInput: 1000,
237
+ totalMatched: 950,
238
+ totalExcluded: 50,
239
239
  },
240
240
  ]),
241
241
  },
@@ -248,9 +248,9 @@ export const LowMatchRate: Story = {
248
248
  apiClient: createMockApiClient([
249
249
  {
250
250
  ...completedRun,
251
- total_input: 10000,
252
- total_matched: 12,
253
- total_excluded: 9988,
251
+ totalInput: 10000,
252
+ totalMatched: 12,
253
+ totalExcluded: 9988,
254
254
  },
255
255
  ]),
256
256
  },
@@ -263,9 +263,9 @@ export const ZeroMatches: Story = {
263
263
  apiClient: createMockApiClient([
264
264
  {
265
265
  ...completedRun,
266
- total_input: 1000,
267
- total_matched: 0,
268
- total_excluded: 1000,
266
+ totalInput: 1000,
267
+ totalMatched: 0,
268
+ totalExcluded: 1000,
269
269
  },
270
270
  ]),
271
271
  },
@@ -32,43 +32,43 @@ const createMockRun = (
32
32
  ): FunnelRun => {
33
33
  const stageStats: Record<string, StageStats> = {
34
34
  'stage-1': {
35
- stage_id: 'stage-1',
36
- stage_name: 'High ICP Score',
37
- input_count: 1000,
38
- matched_count: 500,
39
- not_matched_count: 500,
40
- excluded_count: 0,
41
- tagged_count: 0,
42
- continued_count: 500,
43
- duration_ms: 1000,
35
+ stageId: 'stage-1',
36
+ stageName: 'High ICP Score',
37
+ inputCount: 1000,
38
+ matchedCount: 500,
39
+ notMatchedCount: 500,
40
+ excludedCount: 0,
41
+ taggedCount: 0,
42
+ continuedCount: 500,
43
+ durationMs: 1000,
44
44
  },
45
45
  'stage-2': {
46
- stage_id: 'stage-2',
47
- stage_name: 'Frontend Stack',
48
- input_count: 500,
49
- matched_count: 350,
50
- not_matched_count: 150,
51
- excluded_count: 0,
52
- tagged_count: 0,
53
- continued_count: 350,
54
- duration_ms: 800,
46
+ stageId: 'stage-2',
47
+ stageName: 'Frontend Stack',
48
+ inputCount: 500,
49
+ matchedCount: 350,
50
+ notMatchedCount: 150,
51
+ excludedCount: 0,
52
+ taggedCount: 0,
53
+ continuedCount: 350,
54
+ durationMs: 800,
55
55
  },
56
56
  };
57
57
 
58
58
  return {
59
59
  id: 'run-123',
60
- funnel_id: 'funnel-456',
60
+ funnelId: 'funnel-456',
61
61
  status: 'completed',
62
- trigger_type: 'manual',
63
- triggered_by: 'user-789',
64
- started_at: new Date().toISOString(),
65
- completed_at: new Date().toISOString(),
66
- duration_ms: 2300,
67
- total_input: 1000,
68
- total_matched: 350,
69
- total_excluded: 150,
70
- total_tagged: 0,
71
- stage_stats: stageStats,
62
+ triggerType: 'manual',
63
+ triggeredBy: 'user-789',
64
+ startedAt: new Date().toISOString(),
65
+ completedAt: new Date().toISOString(),
66
+ durationMs: 2300,
67
+ totalInput: 1000,
68
+ totalMatched: 350,
69
+ totalExcluded: 150,
70
+ totalTagged: 0,
71
+ stageStats: stageStats,
72
72
  ...overrides,
73
73
  };
74
74
  };
@@ -97,15 +97,15 @@ describe('FunnelRunHistory Component Logic', () => {
97
97
  await getFunnelRuns('funnel-456', {
98
98
  status: 'completed',
99
99
  page: 1,
100
- page_size: 10,
101
- ordering: '-started_at',
100
+ pageSize: 10,
101
+ ordering: '-startedAt',
102
102
  });
103
103
 
104
104
  expect(getFunnelRuns).toHaveBeenCalledWith('funnel-456', {
105
105
  status: 'completed',
106
106
  page: 1,
107
- page_size: 10,
108
- ordering: '-started_at',
107
+ pageSize: 10,
108
+ ordering: '-startedAt',
109
109
  });
110
110
  });
111
111
 
@@ -113,17 +113,17 @@ describe('FunnelRunHistory Component Logic', () => {
113
113
  const getFunnelRuns = mockApiClient.getFunnelRuns as any;
114
114
 
115
115
  await getFunnelRuns('funnel-456', {
116
- trigger_type: 'scheduled',
116
+ triggerType: 'scheduled',
117
117
  page: 1,
118
- page_size: 10,
119
- ordering: '-started_at',
118
+ pageSize: 10,
119
+ ordering: '-startedAt',
120
120
  });
121
121
 
122
122
  expect(getFunnelRuns).toHaveBeenCalledWith('funnel-456', {
123
- trigger_type: 'scheduled',
123
+ triggerType: 'scheduled',
124
124
  page: 1,
125
- page_size: 10,
126
- ordering: '-started_at',
125
+ pageSize: 10,
126
+ ordering: '-startedAt',
127
127
  });
128
128
  });
129
129
  });
@@ -134,12 +134,12 @@ describe('FunnelRunHistory Component Logic', () => {
134
134
  const runFunnel = mockApiClient.runFunnel as any;
135
135
 
136
136
  await runFunnel('funnel-456', {
137
- trigger_type: 'manual',
137
+ triggerType: 'manual',
138
138
  metadata: { re_run_of: 'run-123' },
139
139
  });
140
140
 
141
141
  expect(runFunnel).toHaveBeenCalledWith('funnel-456', {
142
- trigger_type: 'manual',
142
+ triggerType: 'manual',
143
143
  metadata: { re_run_of: 'run-123' },
144
144
  });
145
145
  });
@@ -210,19 +210,19 @@ describe('Run Status Scenarios', () => {
210
210
  it('should handle completed run', () => {
211
211
  const run = createMockRun({
212
212
  status: 'completed',
213
- total_input: 1000,
214
- total_matched: 235,
213
+ totalInput: 1000,
214
+ totalMatched: 235,
215
215
  });
216
216
 
217
217
  expect(run.status).toBe('completed');
218
- expect(run.total_matched).toBe(235);
218
+ expect(run.totalMatched).toBe(235);
219
219
  });
220
220
 
221
221
  it('should handle failed run', () => {
222
222
  const run = createMockRun({
223
223
  status: 'failed',
224
224
  error: 'Database connection timeout',
225
- total_matched: 0,
225
+ totalMatched: 0,
226
226
  });
227
227
 
228
228
  expect(run.status).toBe('failed');
@@ -232,19 +232,19 @@ describe('Run Status Scenarios', () => {
232
232
  it('should handle running run', () => {
233
233
  const run = createMockRun({
234
234
  status: 'running',
235
- completed_at: undefined,
236
- duration_ms: undefined,
235
+ completedAt: undefined,
236
+ durationMs: undefined,
237
237
  });
238
238
 
239
239
  expect(run.status).toBe('running');
240
- expect(run.completed_at).toBeUndefined();
240
+ expect(run.completedAt).toBeUndefined();
241
241
  });
242
242
 
243
243
  it('should handle pending run', () => {
244
244
  const run = createMockRun({
245
245
  status: 'pending',
246
- started_at: new Date().toISOString(),
247
- completed_at: undefined,
246
+ startedAt: new Date().toISOString(),
247
+ completedAt: undefined,
248
248
  });
249
249
 
250
250
  expect(run.status).toBe('pending');
@@ -273,12 +273,12 @@ describe('Filter Scenarios', () => {
273
273
 
274
274
  it('should filter by manual trigger', () => {
275
275
  const runs = [
276
- createMockRun({ id: '1', trigger_type: 'manual' }),
277
- createMockRun({ id: '2', trigger_type: 'scheduled' }),
278
- createMockRun({ id: '3', trigger_type: 'manual' }),
276
+ createMockRun({ id: '1', triggerType: 'manual' }),
277
+ createMockRun({ id: '2', triggerType: 'scheduled' }),
278
+ createMockRun({ id: '3', triggerType: 'manual' }),
279
279
  ];
280
280
 
281
- const filtered = runs.filter((r) => r.trigger_type === 'manual');
281
+ const filtered = runs.filter((r) => r.triggerType === 'manual');
282
282
  expect(filtered).toHaveLength(2);
283
283
  });
284
284
  });
@@ -291,21 +291,21 @@ describe('Edge Cases', () => {
291
291
 
292
292
  it('should handle run with no stage stats', () => {
293
293
  const run = createMockRun({
294
- stage_stats: {},
294
+ stageStats: {},
295
295
  });
296
296
 
297
- const stageStatsArray = Object.values(run.stage_stats);
297
+ const stageStatsArray = Object.values(run.stageStats);
298
298
  expect(stageStatsArray).toHaveLength(0);
299
299
  });
300
300
 
301
301
  it('should handle run with zero match rate', () => {
302
302
  const run = createMockRun({
303
- total_input: 1000,
304
- total_matched: 0,
303
+ totalInput: 1000,
304
+ totalMatched: 0,
305
305
  });
306
306
 
307
- const matchRate = run.total_input > 0
308
- ? Math.round((run.total_matched / run.total_input) * 100)
307
+ const matchRate = run.totalInput > 0
308
+ ? Math.round((run.totalMatched / run.totalInput) * 100)
309
309
  : 0;
310
310
 
311
311
  expect(matchRate).toBe(0);
@@ -313,11 +313,11 @@ describe('Edge Cases', () => {
313
313
 
314
314
  it('should handle run with 100% match rate', () => {
315
315
  const run = createMockRun({
316
- total_input: 1000,
317
- total_matched: 1000,
316
+ totalInput: 1000,
317
+ totalMatched: 1000,
318
318
  });
319
319
 
320
- const matchRate = Math.round((run.total_matched / run.total_input) * 100);
320
+ const matchRate = Math.round((run.totalMatched / run.totalInput) * 100);
321
321
  expect(matchRate).toBe(100);
322
322
  });
323
323
  });
@@ -61,12 +61,12 @@ export function FunnelRunHistory({
61
61
  const [selectedRun, setSelectedRun] = useState<FunnelRun | null>(null);
62
62
  const [filters, setFilters] = useState<RunFiltersType>({
63
63
  status: 'all',
64
- trigger_type: 'all',
65
- date_range: 'month',
64
+ triggerType: 'all',
65
+ dateRange: 'month',
66
66
  });
67
67
  const [pagination, setPagination] = useState<Pagination>({
68
68
  page: 1,
69
- page_size: 10,
69
+ pageSize: 10,
70
70
  total: 0,
71
71
  });
72
72
  const [isRefreshing, setIsRefreshing] = useState(false);
@@ -77,16 +77,16 @@ export function FunnelRunHistory({
77
77
  setError(null);
78
78
  const params: any = {
79
79
  page: pagination.page,
80
- page_size: pagination.page_size,
81
- ordering: '-started_at', // Most recent first
80
+ pageSize: pagination.pageSize,
81
+ ordering: '-startedAt', // Most recent first
82
82
  };
83
83
 
84
84
  // Add filters
85
85
  if (filters.status && filters.status !== 'all') {
86
86
  params.status = filters.status;
87
87
  }
88
- if (filters.trigger_type && filters.trigger_type !== 'all') {
89
- params.trigger_type = filters.trigger_type;
88
+ if (filters.triggerType && filters.triggerType !== 'all') {
89
+ params.triggerType = filters.triggerType;
90
90
  }
91
91
 
92
92
  const response = await apiClient.getFunnelRuns(funnelId, params);
@@ -103,7 +103,7 @@ export function FunnelRunHistory({
103
103
  setIsLoading(false);
104
104
  setIsRefreshing(false);
105
105
  }
106
- }, [funnelId, apiClient, pagination.page, pagination.page_size, filters]);
106
+ }, [funnelId, apiClient, pagination.page, pagination.pageSize, filters]);
107
107
 
108
108
  // Load runs on mount and when dependencies change
109
109
  useEffect(() => {
@@ -136,7 +136,7 @@ export function FunnelRunHistory({
136
136
  const handleReRun = async (run: FunnelRun) => {
137
137
  try {
138
138
  await apiClient.runFunnel(funnelId, {
139
- trigger_type: 'manual',
139
+ triggerType: 'manual',
140
140
  metadata: { re_run_of: run.id },
141
141
  });
142
142
  // Refresh to show new run
@@ -168,7 +168,7 @@ export function FunnelRunHistory({
168
168
  };
169
169
 
170
170
  // Pagination
171
- const totalPages = Math.ceil(pagination.total / pagination.page_size);
171
+ const totalPages = Math.ceil(pagination.total / pagination.pageSize);
172
172
  const canGoBack = pagination.page > 1;
173
173
  const canGoForward = pagination.page < totalPages;
174
174
 
@@ -184,9 +184,9 @@ export function FunnelRunHistory({
184
184
  }
185
185
  };
186
186
 
187
- const startIndex = (pagination.page - 1) * pagination.page_size + 1;
187
+ const startIndex = (pagination.page - 1) * pagination.pageSize + 1;
188
188
  const endIndex = Math.min(
189
- pagination.page * pagination.page_size,
189
+ pagination.page * pagination.pageSize,
190
190
  pagination.total
191
191
  );
192
192
 
@@ -90,7 +90,7 @@ export function RunDetailsModal({
90
90
 
91
91
  if (!run) return null;
92
92
 
93
- const stageStatsArray = Object.values(run.stage_stats);
93
+ const stageStatsArray = Object.values(run.stageStats);
94
94
 
95
95
  return (
96
96
  <div
@@ -139,21 +139,21 @@ export function RunDetailsModal({
139
139
  <div>
140
140
  <div className="text-xs text-gray-600 mb-1">Duration</div>
141
141
  <div className="text-sm font-medium text-gray-900">
142
- {formatDuration(run.duration_ms)}
142
+ {formatDuration(run.durationMs)}
143
143
  </div>
144
144
  </div>
145
145
  <div>
146
146
  <div className="text-xs text-gray-600 mb-1">Started</div>
147
147
  <div className="text-sm font-medium text-gray-900">
148
- <span title={formatFullTimestamp(run.started_at)}>
149
- {formatRelativeTime(run.started_at)}
148
+ <span title={formatFullTimestamp(run.startedAt)}>
149
+ {formatRelativeTime(run.startedAt)}
150
150
  </span>
151
151
  </div>
152
152
  </div>
153
153
  <div>
154
154
  <div className="text-xs text-gray-600 mb-1">Triggered by</div>
155
155
  <div className="text-sm font-medium text-gray-900">
156
- {run.triggered_by || 'System'} ({run.trigger_type})
156
+ {run.triggeredBy || 'System'} ({run.triggerType})
157
157
  </div>
158
158
  </div>
159
159
  </div>
@@ -39,15 +39,15 @@ export function RunFilters({
39
39
  const clearFilters = () => {
40
40
  onFiltersChange({
41
41
  status: 'all',
42
- trigger_type: 'all',
43
- date_range: 'month',
42
+ triggerType: 'all',
43
+ dateRange: 'month',
44
44
  });
45
45
  };
46
46
 
47
47
  const hasActiveFilters =
48
48
  filters.status !== 'all' ||
49
- filters.trigger_type !== 'all' ||
50
- filters.date_range !== 'month';
49
+ filters.triggerType !== 'all' ||
50
+ filters.dateRange !== 'month';
51
51
 
52
52
  return (
53
53
  <div
@@ -82,9 +82,9 @@ export function RunFilters({
82
82
  </label>
83
83
  <select
84
84
  id="trigger-filter"
85
- value={filters.trigger_type || 'all'}
85
+ value={filters.triggerType || 'all'}
86
86
  onChange={(e) =>
87
- updateFilter('trigger_type', e.target.value as TriggerType | 'all')
87
+ updateFilter('triggerType', e.target.value as TriggerType | 'all')
88
88
  }
89
89
  className="px-2 py-1 text-sm border border-gray-300 rounded-md bg-white focus:outline-none focus:ring-2 focus:ring-blue-500"
90
90
  >
@@ -103,8 +103,8 @@ export function RunFilters({
103
103
  </label>
104
104
  <select
105
105
  id="date-filter"
106
- value={filters.date_range || 'month'}
107
- onChange={(e) => updateFilter('date_range', e.target.value as any)}
106
+ value={filters.dateRange || 'month'}
107
+ onChange={(e) => updateFilter('dateRange', e.target.value as any)}
108
108
  className="px-2 py-1 text-sm border border-gray-300 rounded-md bg-white focus:outline-none focus:ring-2 focus:ring-blue-500"
109
109
  >
110
110
  <option value="all">All time</option>