@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
@@ -41,21 +41,21 @@ const createStage = (
41
41
  order,
42
42
  name,
43
43
  description: `${name} stage filters entities`,
44
- filter_logic: 'AND',
44
+ filterLogic: 'AND',
45
45
  rules: [
46
46
  {
47
- field_path: 'score',
47
+ fieldPath: 'score',
48
48
  operator: 'gte',
49
49
  value: 50,
50
50
  },
51
51
  {
52
- field_path: 'status',
52
+ fieldPath: 'status',
53
53
  operator: 'eq',
54
54
  value: 'active',
55
55
  },
56
56
  ],
57
- match_action: matchAction,
58
- no_match_action: noMatchAction,
57
+ matchAction: matchAction,
58
+ noMatchAction: noMatchAction,
59
59
  });
60
60
 
61
61
  // Helper to create funnel
@@ -67,10 +67,10 @@ const createFunnel = (
67
67
  name,
68
68
  description: 'A sample funnel for visualization',
69
69
  status: 'active',
70
- input_type: 'contacts',
70
+ inputType: 'contacts',
71
71
  stages,
72
- created_at: new Date().toISOString(),
73
- updated_at: new Date().toISOString(),
72
+ createdAt: new Date().toISOString(),
73
+ updatedAt: new Date().toISOString(),
74
74
  });
75
75
 
76
76
  // Empty state
@@ -134,46 +134,46 @@ export const WithRunData: Story = {
134
134
  ),
135
135
  runData: {
136
136
  id: 'run-1',
137
- funnel_id: 'funnel-1',
137
+ funnelId: 'funnel-1',
138
138
  status: 'completed',
139
- trigger_type: 'manual',
140
- started_at: new Date().toISOString(),
141
- completed_at: new Date().toISOString(),
142
- duration_ms: 1234,
143
- total_input: 1000,
144
- total_matched: 235,
145
- total_excluded: 765,
146
- total_tagged: 0,
147
- stage_stats: {
139
+ triggerType: 'manual',
140
+ startedAt: new Date().toISOString(),
141
+ completedAt: new Date().toISOString(),
142
+ durationMs: 1234,
143
+ totalInput: 1000,
144
+ totalMatched: 235,
145
+ totalExcluded: 765,
146
+ totalTagged: 0,
147
+ stageStats: {
148
148
  'stage-1': {
149
- stage_id: 'stage-1',
150
- stage_name: 'High ICP Score',
151
- input_count: 1000,
152
- matched_count: 500,
153
- not_matched_count: 500,
154
- excluded_count: 500,
155
- tagged_count: 0,
156
- continued_count: 500,
149
+ stageId: 'stage-1',
150
+ stageName: 'High ICP Score',
151
+ inputCount: 1000,
152
+ matchedCount: 500,
153
+ notMatchedCount: 500,
154
+ excludedCount: 500,
155
+ taggedCount: 0,
156
+ continuedCount: 500,
157
157
  },
158
158
  'stage-2': {
159
- stage_id: 'stage-2',
160
- stage_name: 'Frontend Stack',
161
- input_count: 500,
162
- matched_count: 350,
163
- not_matched_count: 150,
164
- excluded_count: 150,
165
- tagged_count: 0,
166
- continued_count: 350,
159
+ stageId: 'stage-2',
160
+ stageName: 'Frontend Stack',
161
+ inputCount: 500,
162
+ matchedCount: 350,
163
+ notMatchedCount: 150,
164
+ excludedCount: 150,
165
+ taggedCount: 0,
166
+ continuedCount: 350,
167
167
  },
168
168
  'stage-3': {
169
- stage_id: 'stage-3',
170
- stage_name: 'Testing Tools',
171
- input_count: 350,
172
- matched_count: 235,
173
- not_matched_count: 115,
174
- excluded_count: 115,
175
- tagged_count: 0,
176
- continued_count: 235,
169
+ stageId: 'stage-3',
170
+ stageName: 'Testing Tools',
171
+ inputCount: 350,
172
+ matchedCount: 235,
173
+ notMatchedCount: 115,
174
+ excludedCount: 115,
175
+ taggedCount: 0,
176
+ continuedCount: 235,
177
177
  },
178
178
  },
179
179
  } as FunnelRun,
@@ -51,16 +51,16 @@ describe('FunnelVisualFlow', () => {
51
51
  order,
52
52
  name,
53
53
  description: `Description for ${name}`,
54
- filter_logic: 'AND',
54
+ filterLogic: 'AND',
55
55
  rules: [
56
56
  {
57
- field_path: 'test',
57
+ fieldPath: 'test',
58
58
  operator: 'eq',
59
59
  value: 'test',
60
60
  },
61
61
  ],
62
- match_action: 'continue',
63
- no_match_action: 'exclude',
62
+ matchAction: 'continue',
63
+ noMatchAction: 'exclude',
64
64
  });
65
65
 
66
66
  const createFunnel = (stages: FunnelStage[]): Funnel => ({
@@ -68,10 +68,10 @@ describe('FunnelVisualFlow', () => {
68
68
  name: 'Test Funnel',
69
69
  description: 'Test funnel description',
70
70
  status: 'active',
71
- input_type: 'contacts',
71
+ inputType: 'contacts',
72
72
  stages,
73
- created_at: new Date().toISOString(),
74
- updated_at: new Date().toISOString(),
73
+ createdAt: new Date().toISOString(),
74
+ updatedAt: new Date().toISOString(),
75
75
  });
76
76
 
77
77
  it('renders empty state when no stages', () => {
@@ -119,34 +119,34 @@ describe('FunnelVisualFlow', () => {
119
119
 
120
120
  const runData: FunnelRun = {
121
121
  id: 'run-1',
122
- funnel_id: 'funnel-1',
122
+ funnelId: 'funnel-1',
123
123
  status: 'completed',
124
- trigger_type: 'manual',
125
- started_at: new Date().toISOString(),
126
- total_input: 1000,
127
- total_matched: 350,
128
- total_excluded: 650,
129
- total_tagged: 0,
130
- stage_stats: {
124
+ triggerType: 'manual',
125
+ startedAt: new Date().toISOString(),
126
+ totalInput: 1000,
127
+ totalMatched: 350,
128
+ totalExcluded: 650,
129
+ totalTagged: 0,
130
+ stageStats: {
131
131
  'stage-1': {
132
- stage_id: 'stage-1',
133
- stage_name: 'High ICP Score',
134
- input_count: 1000,
135
- matched_count: 500,
136
- not_matched_count: 500,
137
- excluded_count: 500,
138
- tagged_count: 0,
139
- continued_count: 500,
132
+ stageId: 'stage-1',
133
+ stageName: 'High ICP Score',
134
+ inputCount: 1000,
135
+ matchedCount: 500,
136
+ notMatchedCount: 500,
137
+ excludedCount: 500,
138
+ taggedCount: 0,
139
+ continuedCount: 500,
140
140
  },
141
141
  'stage-2': {
142
- stage_id: 'stage-2',
143
- stage_name: 'Frontend Stack',
144
- input_count: 500,
145
- matched_count: 350,
146
- not_matched_count: 150,
147
- excluded_count: 150,
148
- tagged_count: 0,
149
- continued_count: 350,
142
+ stageId: 'stage-2',
143
+ stageName: 'Frontend Stack',
144
+ inputCount: 500,
145
+ matchedCount: 350,
146
+ notMatchedCount: 150,
147
+ excludedCount: 150,
148
+ taggedCount: 0,
149
+ continuedCount: 350,
150
150
  },
151
151
  },
152
152
  };
@@ -92,13 +92,13 @@ function getExcludedCount(
92
92
  fromStageId: string,
93
93
  toStageId: string
94
94
  ): number {
95
- const fromStats = runData.stage_stats[fromStageId];
96
- const toStats = runData.stage_stats[toStageId];
95
+ const fromStats = runData.stageStats[fromStageId];
96
+ const toStats = runData.stageStats[toStageId];
97
97
 
98
98
  if (!fromStats || !toStats) return 0;
99
99
 
100
100
  // Calculate how many entities didn't continue to next stage
101
- return fromStats.continued_count - toStats.input_count;
101
+ return fromStats.continuedCount - toStats.inputCount;
102
102
  }
103
103
 
104
104
  /**
@@ -131,7 +131,7 @@ export function FunnelVisualFlow({
131
131
  // Build nodes from funnel stages
132
132
  const initialNodes: Node[] = useMemo(() => {
133
133
  return funnel.stages.map((stage, index) => {
134
- const stats = runData?.stage_stats?.[stage.id];
134
+ const stats = runData?.stageStats?.[stage.id];
135
135
 
136
136
  return {
137
137
  id: stage.id,
@@ -43,8 +43,8 @@ export interface StageNodeData {
43
43
  */
44
44
  function getStageColor(stage: FunnelStage): string {
45
45
  // Primary action determines color
46
- const matchAction = stage.match_action;
47
- const noMatchAction = stage.no_match_action;
46
+ const matchAction = stage.matchAction;
47
+ const noMatchAction = stage.noMatchAction;
48
48
 
49
49
  // Output stages are green
50
50
  if (matchAction === 'output') {
@@ -69,8 +69,8 @@ function getStageColor(stage: FunnelStage): string {
69
69
  * Get action label for display
70
70
  */
71
71
  function getActionLabel(stage: FunnelStage): string {
72
- const matchAction = stage.match_action;
73
- const noMatchAction = stage.no_match_action;
72
+ const matchAction = stage.matchAction;
73
+ const noMatchAction = stage.noMatchAction;
74
74
 
75
75
  if (matchAction === 'output') return 'Output';
76
76
  if (noMatchAction === 'exclude') return 'Exclude Non-Matches';
@@ -147,19 +147,19 @@ export function StageNode({ data }: NodeProps) {
147
147
  <div className="stage-stats">
148
148
  <div className="stat-row">
149
149
  <span className="stat-label">Input:</span>
150
- <span className="stat-value">{stats.input_count}</span>
150
+ <span className="stat-value">{stats.inputCount}</span>
151
151
  </div>
152
152
  <div className="stat-row">
153
153
  <span className="stat-label">Matched:</span>
154
154
  <span className="stat-value text-green-600">
155
- {stats.matched_count}
155
+ {stats.matchedCount}
156
156
  </span>
157
157
  </div>
158
- {stats.excluded_count > 0 && (
158
+ {stats.excludedCount > 0 && (
159
159
  <div className="stat-row">
160
160
  <span className="stat-label">Excluded:</span>
161
161
  <span className="stat-value text-red-600">
162
- {stats.excluded_count}
162
+ {stats.excludedCount}
163
163
  </span>
164
164
  </div>
165
165
  )}
@@ -98,9 +98,9 @@ export function FunnelDetailsPage({ funnelId }: { funnelId: string }) {
98
98
  <div className="run-info">
99
99
  <h2>Latest Run</h2>
100
100
  <p>Status: {latestRun.status}</p>
101
- <p>Started: {new Date(latestRun.started_at).toLocaleString()}</p>
101
+ <p>Started: {new Date(latestRun.startedAt).toLocaleString()}</p>
102
102
  <p>
103
- Results: {latestRun.total_matched} / {latestRun.total_input} matched
103
+ Results: {latestRun.totalMatched} / {latestRun.totalInput} matched
104
104
  </p>
105
105
  </div>
106
106
  )}
@@ -133,20 +133,20 @@ function StageDetailsModal({
133
133
  <ul>
134
134
  {stage.rules.map((rule, index) => (
135
135
  <li key={index}>
136
- {rule.field_path} {rule.operator} {JSON.stringify(rule.value)}
136
+ {rule.fieldPath} {rule.operator} {JSON.stringify(rule.value)}
137
137
  </li>
138
138
  ))}
139
139
  </ul>
140
140
 
141
141
  <h3>Actions</h3>
142
- <p>Match: {stage.match_action}</p>
143
- <p>No Match: {stage.no_match_action}</p>
142
+ <p>Match: {stage.matchAction}</p>
143
+ <p>No Match: {stage.noMatchAction}</p>
144
144
 
145
- {stage.match_tags && stage.match_tags.length > 0 && (
145
+ {stage.matchTags && stage.matchTags.length > 0 && (
146
146
  <>
147
147
  <h3>Match Tags</h3>
148
148
  <ul>
149
- {stage.match_tags.map((tag) => (
149
+ {stage.matchTags.map((tag) => (
150
150
  <li key={tag}>{tag}</li>
151
151
  ))}
152
152
  </ul>