@startsimpli/funnels 0.1.3 → 0.1.5

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 (151) hide show
  1. package/package.json +9 -31
  2. package/src/api/README.md +507 -0
  3. package/src/api/adapter.ts +106 -0
  4. package/src/api/client.test.ts +640 -0
  5. package/src/api/client.ts +385 -0
  6. package/src/api/default-adapter.ts +243 -0
  7. package/src/api/index.ts +24 -0
  8. package/src/components/FilterRuleEditor/ARCHITECTURE.md +354 -0
  9. package/src/components/FilterRuleEditor/FieldSelector.tsx +91 -0
  10. package/src/components/FilterRuleEditor/FilterRuleEditor.stories.tsx +462 -0
  11. package/src/components/FilterRuleEditor/FilterRuleEditor.test.tsx +520 -0
  12. package/src/components/FilterRuleEditor/FilterRuleEditor.tsx +225 -0
  13. package/src/components/FilterRuleEditor/LogicToggle.tsx +64 -0
  14. package/src/components/FilterRuleEditor/OperatorSelector.tsx +75 -0
  15. package/src/components/FilterRuleEditor/README.md +291 -0
  16. package/src/components/FilterRuleEditor/RuleRow.tsx +246 -0
  17. package/src/components/FilterRuleEditor/ValueInputs/BooleanValueInput.tsx +54 -0
  18. package/src/components/FilterRuleEditor/ValueInputs/ChoiceValueInput.tsx +83 -0
  19. package/src/components/FilterRuleEditor/ValueInputs/DateValueInput.tsx +70 -0
  20. package/src/components/FilterRuleEditor/ValueInputs/MultiChoiceValueInput.tsx +132 -0
  21. package/src/components/FilterRuleEditor/ValueInputs/NumberValueInput.tsx +73 -0
  22. package/src/components/FilterRuleEditor/ValueInputs/TextValueInput.tsx +50 -0
  23. package/src/components/FilterRuleEditor/ValueInputs/index.ts +12 -0
  24. package/src/components/FilterRuleEditor/constants.ts +64 -0
  25. package/src/components/FilterRuleEditor/index.ts +14 -0
  26. package/src/components/FunnelCard/DESIGN.md +447 -0
  27. package/src/components/FunnelCard/FunnelCard.stories.tsx +484 -0
  28. package/src/components/FunnelCard/FunnelCard.test.ts +257 -0
  29. package/src/components/FunnelCard/FunnelCard.test.tsx +336 -0
  30. package/src/components/FunnelCard/FunnelCard.tsx +204 -0
  31. package/src/components/FunnelCard/FunnelStats.tsx +68 -0
  32. package/src/components/FunnelCard/IMPLEMENTATION_SUMMARY.md +505 -0
  33. package/src/components/FunnelCard/INSTALLATION.md +304 -0
  34. package/src/components/FunnelCard/MatchBar.tsx +49 -0
  35. package/src/components/FunnelCard/README.md +294 -0
  36. package/src/components/FunnelCard/StageIndicator.tsx +62 -0
  37. package/src/components/FunnelCard/StatusBadge.tsx +52 -0
  38. package/src/components/FunnelCard/index.ts +14 -0
  39. package/src/components/FunnelPreview/EntityCard.tsx +72 -0
  40. package/src/components/FunnelPreview/FunnelPreview.stories.tsx +227 -0
  41. package/src/components/FunnelPreview/FunnelPreview.test.tsx +316 -0
  42. package/src/components/FunnelPreview/FunnelPreview.tsx +249 -0
  43. package/src/components/FunnelPreview/LoadingPreview.tsx +60 -0
  44. package/src/components/FunnelPreview/PreviewStats.tsx +78 -0
  45. package/src/components/FunnelPreview/README.md +337 -0
  46. package/src/components/FunnelPreview/StageBreakdown.tsx +94 -0
  47. package/src/components/FunnelPreview/example.tsx +286 -0
  48. package/src/components/FunnelPreview/index.ts +14 -0
  49. package/src/components/FunnelRunHistory/COMPONENT_SUMMARY.md +246 -0
  50. package/src/components/FunnelRunHistory/FunnelRunHistory.stories.tsx +272 -0
  51. package/src/components/FunnelRunHistory/FunnelRunHistory.test.tsx +323 -0
  52. package/src/components/FunnelRunHistory/FunnelRunHistory.tsx +329 -0
  53. package/src/components/FunnelRunHistory/README.md +325 -0
  54. package/src/components/FunnelRunHistory/RunActions.tsx +168 -0
  55. package/src/components/FunnelRunHistory/RunDetailsModal.tsx +221 -0
  56. package/src/components/FunnelRunHistory/RunFilters.tsx +128 -0
  57. package/src/components/FunnelRunHistory/RunRow.tsx +122 -0
  58. package/src/components/FunnelRunHistory/RunStatusBadge.tsx +75 -0
  59. package/src/components/FunnelRunHistory/StageBreakdownList.tsx +110 -0
  60. package/src/components/FunnelRunHistory/index.ts +51 -0
  61. package/src/components/FunnelRunHistory/types.ts +40 -0
  62. package/src/components/FunnelRunHistory/utils.test.ts +126 -0
  63. package/src/components/FunnelRunHistory/utils.ts +100 -0
  64. package/src/components/FunnelStageBuilder/AddStageButton.tsx +52 -0
  65. package/src/components/FunnelStageBuilder/FunnelStageBuilder.css +413 -0
  66. package/src/components/FunnelStageBuilder/FunnelStageBuilder.stories.tsx +312 -0
  67. package/src/components/FunnelStageBuilder/FunnelStageBuilder.test.tsx +304 -0
  68. package/src/components/FunnelStageBuilder/FunnelStageBuilder.tsx +321 -0
  69. package/src/components/FunnelStageBuilder/README.md +341 -0
  70. package/src/components/FunnelStageBuilder/StageActions.test.tsx +205 -0
  71. package/src/components/FunnelStageBuilder/StageActions.tsx +126 -0
  72. package/src/components/FunnelStageBuilder/StageCard.tsx +202 -0
  73. package/src/components/FunnelStageBuilder/StageForm.tsx +262 -0
  74. package/src/components/FunnelStageBuilder/TagInput.test.tsx +178 -0
  75. package/src/components/FunnelStageBuilder/TagInput.tsx +129 -0
  76. package/src/components/FunnelStageBuilder/index.ts +21 -0
  77. package/src/components/FunnelVisualFlow/FlowLegend.tsx +77 -0
  78. package/{dist/components/index.css → src/components/FunnelVisualFlow/FunnelVisualFlow.css} +89 -13
  79. package/src/components/FunnelVisualFlow/FunnelVisualFlow.stories.tsx +254 -0
  80. package/src/components/FunnelVisualFlow/FunnelVisualFlow.test.tsx +208 -0
  81. package/src/components/FunnelVisualFlow/FunnelVisualFlow.tsx +229 -0
  82. package/src/components/FunnelVisualFlow/README.md +323 -0
  83. package/src/components/FunnelVisualFlow/StageNode.tsx +188 -0
  84. package/src/components/FunnelVisualFlow/example.tsx +227 -0
  85. package/src/components/FunnelVisualFlow/index.ts +10 -0
  86. package/src/components/index.ts +102 -0
  87. package/src/core/README.md +307 -0
  88. package/src/core/engine.test.ts +1087 -0
  89. package/src/core/engine.ts +329 -0
  90. package/src/core/evaluator.example.ts +353 -0
  91. package/src/core/evaluator.test.ts +639 -0
  92. package/src/core/evaluator.ts +261 -0
  93. package/src/core/field-resolver.example.ts +175 -0
  94. package/src/core/field-resolver.test.ts +541 -0
  95. package/src/core/field-resolver.ts +247 -0
  96. package/src/core/index.ts +34 -0
  97. package/src/core/operators.test.ts +539 -0
  98. package/src/core/operators.ts +241 -0
  99. package/src/hooks/index.ts +5 -0
  100. package/src/hooks/useDebouncedValue.ts +28 -0
  101. package/src/index.ts +155 -0
  102. package/src/store/README.md +342 -0
  103. package/src/store/create-funnel-store.test.ts +686 -0
  104. package/src/store/create-funnel-store.ts +538 -0
  105. package/src/store/index.ts +9 -0
  106. package/src/store/types.ts +294 -0
  107. package/src/stories/CrossDomain.stories.tsx +149 -0
  108. package/src/stories/Welcome.stories.tsx +81 -0
  109. package/src/stories/demo-data/index.ts +3 -0
  110. package/src/stories/demo-data/investors.ts +216 -0
  111. package/src/stories/demo-data/leads.ts +223 -0
  112. package/src/stories/demo-data/recipes.ts +217 -0
  113. package/src/test/setup.ts +5 -0
  114. package/src/types/index.ts +843 -0
  115. package/dist/client-3ESO2NHy.d.ts +0 -310
  116. package/dist/client-CZu03ACp.d.cts +0 -310
  117. package/dist/components/index.cjs +0 -3243
  118. package/dist/components/index.cjs.map +0 -1
  119. package/dist/components/index.css.map +0 -1
  120. package/dist/components/index.d.cts +0 -726
  121. package/dist/components/index.d.ts +0 -726
  122. package/dist/components/index.js +0 -3196
  123. package/dist/components/index.js.map +0 -1
  124. package/dist/core/index.cjs +0 -500
  125. package/dist/core/index.cjs.map +0 -1
  126. package/dist/core/index.d.cts +0 -359
  127. package/dist/core/index.d.ts +0 -359
  128. package/dist/core/index.js +0 -486
  129. package/dist/core/index.js.map +0 -1
  130. package/dist/hooks/index.cjs +0 -21
  131. package/dist/hooks/index.cjs.map +0 -1
  132. package/dist/hooks/index.d.cts +0 -11
  133. package/dist/hooks/index.d.ts +0 -11
  134. package/dist/hooks/index.js +0 -19
  135. package/dist/hooks/index.js.map +0 -1
  136. package/dist/index-BGDEXbuz.d.cts +0 -434
  137. package/dist/index-BGDEXbuz.d.ts +0 -434
  138. package/dist/index.cjs +0 -4499
  139. package/dist/index.cjs.map +0 -1
  140. package/dist/index.css +0 -198
  141. package/dist/index.css.map +0 -1
  142. package/dist/index.d.cts +0 -99
  143. package/dist/index.d.ts +0 -99
  144. package/dist/index.js +0 -4421
  145. package/dist/index.js.map +0 -1
  146. package/dist/store/index.cjs +0 -391
  147. package/dist/store/index.cjs.map +0 -1
  148. package/dist/store/index.d.cts +0 -225
  149. package/dist/store/index.d.ts +0 -225
  150. package/dist/store/index.js +0 -388
  151. package/dist/store/index.js.map +0 -1
@@ -0,0 +1,342 @@
1
+ # Funnel Store
2
+
3
+ Zustand state management for @simpli/funnels.
4
+
5
+ ## Features
6
+
7
+ - **Factory Pattern** - Create isolated store instances for multi-editor scenarios
8
+ - **Optimistic Updates** - UI updates immediately, rollback on error
9
+ - **Dirty Tracking** - Track unsaved changes
10
+ - **Loading States** - Better UX with loading indicators
11
+ - **Error Handling** - Automatic error capture and rollback
12
+ - **Generic** - Works with any entity type (no product-specific logic)
13
+
14
+ ## Usage
15
+
16
+ ### Basic Setup
17
+
18
+ ```typescript
19
+ import { createFunnelStore } from '@simpli/funnels';
20
+ import { FetchAdapter, FunnelApiClient } from '@simpli/funnels';
21
+
22
+ // Create API client
23
+ const adapter = new FetchAdapter({
24
+ headers: {
25
+ 'Authorization': 'Bearer your-token',
26
+ },
27
+ });
28
+
29
+ const apiClient = new FunnelApiClient(adapter, 'https://api.example.com');
30
+
31
+ // Create store instance
32
+ const useFunnelStore = createFunnelStore(apiClient);
33
+ ```
34
+
35
+ ### In React Components
36
+
37
+ ```typescript
38
+ function FunnelEditor() {
39
+ const {
40
+ funnels,
41
+ selectedFunnel,
42
+ isLoading,
43
+ error,
44
+ loadFunnels,
45
+ selectFunnel,
46
+ updateFunnel,
47
+ } = useFunnelStore();
48
+
49
+ useEffect(() => {
50
+ loadFunnels({ status: 'active' });
51
+ }, [loadFunnels]);
52
+
53
+ const handleSave = async () => {
54
+ if (!selectedFunnel) return;
55
+
56
+ try {
57
+ await updateFunnel(selectedFunnel.id, {
58
+ name: 'Updated Name',
59
+ });
60
+ } catch (error) {
61
+ // Error handled automatically, UI rolled back
62
+ console.error('Failed to save:', error);
63
+ }
64
+ };
65
+
66
+ if (isLoading) return <div>Loading...</div>;
67
+ if (error) return <div>Error: {error.message}</div>;
68
+
69
+ return (
70
+ <div>
71
+ {funnels.map(funnel => (
72
+ <div key={funnel.id} onClick={() => selectFunnel(funnel.id)}>
73
+ {funnel.name}
74
+ </div>
75
+ ))}
76
+ </div>
77
+ );
78
+ }
79
+ ```
80
+
81
+ ### Multiple Store Instances
82
+
83
+ The factory pattern allows multiple independent stores:
84
+
85
+ ```typescript
86
+ // Editor 1
87
+ const editor1ApiClient = new FunnelApiClient(adapter1, baseUrl);
88
+ const useEditor1Store = createFunnelStore(editor1ApiClient);
89
+
90
+ // Editor 2
91
+ const editor2ApiClient = new FunnelApiClient(adapter2, baseUrl);
92
+ const useEditor2Store = createFunnelStore(editor2ApiClient);
93
+
94
+ // Each store is completely isolated
95
+ ```
96
+
97
+ ## API
98
+
99
+ ### State
100
+
101
+ ```typescript
102
+ interface FunnelStore<TEntity = any> {
103
+ // Data
104
+ funnels: Funnel<TEntity>[];
105
+ selectedFunnel: Funnel<TEntity> | null;
106
+ selectedStage: FunnelStage<TEntity> | null;
107
+ runs: FunnelRun[];
108
+ pagination: { count, next, previous, currentPage, pageSize };
109
+
110
+ // UI State
111
+ isLoading: boolean;
112
+ error: Error | null;
113
+ isDirty: boolean;
114
+ }
115
+ ```
116
+
117
+ ### Actions
118
+
119
+ #### Funnel Management
120
+
121
+ ```typescript
122
+ // Load funnels with filters
123
+ await loadFunnels({ status: 'active', page: 1 });
124
+
125
+ // Select funnel for editing
126
+ selectFunnel('funnel-id');
127
+
128
+ // Create new funnel
129
+ const funnel = await createFunnel({
130
+ name: 'My Funnel',
131
+ status: 'draft',
132
+ input_type: 'contacts',
133
+ stages: [],
134
+ });
135
+
136
+ // Update funnel (optimistic)
137
+ await updateFunnel('funnel-id', {
138
+ name: 'Updated Name',
139
+ });
140
+
141
+ // Delete funnel (optimistic)
142
+ await deleteFunnel('funnel-id');
143
+
144
+ // Duplicate funnel
145
+ const copy = await duplicateFunnel('funnel-id');
146
+ ```
147
+
148
+ #### Stage Management
149
+
150
+ ```typescript
151
+ // Select stage
152
+ selectStage('stage-id');
153
+
154
+ // Create stage
155
+ const stage = await createStage('funnel-id', {
156
+ order: 0,
157
+ name: 'Qualification',
158
+ filter_logic: 'AND',
159
+ rules: [],
160
+ match_action: 'continue',
161
+ no_match_action: 'exclude',
162
+ });
163
+
164
+ // Update stage (optimistic)
165
+ await updateStage('funnel-id', 'stage-id', {
166
+ name: 'Updated Name',
167
+ });
168
+
169
+ // Delete stage (optimistic)
170
+ await deleteStage('funnel-id', 'stage-id');
171
+
172
+ // Reorder stages (optimistic)
173
+ await reorderStages('funnel-id', ['stage-2', 'stage-1', 'stage-3']);
174
+ ```
175
+
176
+ #### Run Operations
177
+
178
+ ```typescript
179
+ // Run funnel
180
+ const run = await runFunnel('funnel-id', {
181
+ trigger_type: 'manual',
182
+ });
183
+
184
+ // Load run history
185
+ await loadRuns('funnel-id', {
186
+ status: 'completed',
187
+ page: 1,
188
+ });
189
+
190
+ // Cancel running funnel
191
+ await cancelRun('run-id');
192
+ ```
193
+
194
+ #### UI State
195
+
196
+ ```typescript
197
+ // Set dirty flag
198
+ setDirty(true);
199
+
200
+ // Clear error
201
+ clearError();
202
+
203
+ // Reset store
204
+ reset();
205
+ ```
206
+
207
+ ## Optimistic Updates
208
+
209
+ The store automatically implements optimistic updates for:
210
+ - `updateFunnel` - Update funnel data
211
+ - `deleteFunnel` - Remove funnel
212
+ - `updateStage` - Update stage data
213
+ - `deleteStage` - Remove stage
214
+ - `reorderStages` - Reorder stages
215
+
216
+ On error, the store automatically rolls back to the previous state.
217
+
218
+ ### How It Works
219
+
220
+ ```typescript
221
+ // 1. User clicks save
222
+ await updateFunnel('funnel-1', { name: 'New Name' });
223
+
224
+ // 2. UI updates immediately (optimistic)
225
+ // -> funnels[0].name = 'New Name'
226
+
227
+ // 3. API call happens in background
228
+ // -> POST /api/v1/funnels/funnel-1/
229
+
230
+ // 4a. Success: Update with server response
231
+ // -> funnels[0] = serverResponse
232
+
233
+ // 4b. Error: Rollback to previous state
234
+ // -> funnels[0].name = 'Old Name'
235
+ // -> error = Error('Update failed')
236
+ ```
237
+
238
+ ## Dirty Tracking
239
+
240
+ Track unsaved changes:
241
+
242
+ ```typescript
243
+ const { isDirty, setDirty } = useFunnelStore();
244
+
245
+ // Set dirty when user makes changes
246
+ const handleChange = (value) => {
247
+ setDirty(true);
248
+ };
249
+
250
+ // Clear dirty on save
251
+ const handleSave = async () => {
252
+ await updateFunnel(selectedFunnel.id, data);
253
+ // isDirty automatically set to false
254
+ };
255
+
256
+ // Warn on navigation
257
+ useEffect(() => {
258
+ const handleBeforeUnload = (e) => {
259
+ if (isDirty) {
260
+ e.preventDefault();
261
+ e.returnValue = '';
262
+ }
263
+ };
264
+
265
+ window.addEventListener('beforeunload', handleBeforeUnload);
266
+ return () => window.removeEventListener('beforeunload', handleBeforeUnload);
267
+ }, [isDirty]);
268
+ ```
269
+
270
+ ## Error Handling
271
+
272
+ Errors are automatically captured and stored:
273
+
274
+ ```typescript
275
+ const { error, clearError } = useFunnelStore();
276
+
277
+ // Errors set automatically
278
+ await updateFunnel('bad-id', { name: 'New' });
279
+ // -> error = Error('Not found')
280
+
281
+ // Clear error manually
282
+ clearError();
283
+
284
+ // Or display in UI
285
+ {error && <Alert>{error.message}</Alert>}
286
+ ```
287
+
288
+ ## Testing
289
+
290
+ See `create-funnel-store.test.ts` for comprehensive test examples.
291
+
292
+ ```typescript
293
+ import { createFunnelStore } from '@simpli/funnels';
294
+ import { vi } from 'vitest';
295
+
296
+ // Mock API client
297
+ const mockClient = {
298
+ listFunnels: vi.fn(),
299
+ createFunnel: vi.fn(),
300
+ // ... other methods
301
+ };
302
+
303
+ // Create store with mock
304
+ const useFunnelStore = createFunnelStore(mockClient);
305
+
306
+ // Test actions
307
+ const { loadFunnels } = useFunnelStore.getState();
308
+ await loadFunnels();
309
+
310
+ expect(mockClient.listFunnels).toHaveBeenCalled();
311
+ ```
312
+
313
+ ## TypeScript
314
+
315
+ The store is fully typed with generics:
316
+
317
+ ```typescript
318
+ interface InvestorEntity {
319
+ id: string;
320
+ name: string;
321
+ stage: string;
322
+ }
323
+
324
+ const useFunnelStore = createFunnelStore<InvestorEntity>(apiClient);
325
+
326
+ // TypeScript knows entity type
327
+ const { funnels } = useFunnelStore();
328
+ // funnels: Funnel<InvestorEntity>[]
329
+ ```
330
+
331
+ ## Performance
332
+
333
+ - **Selective Rendering** - Use Zustand selectors to prevent unnecessary re-renders
334
+ - **Pagination** - Load funnels in pages
335
+ - **Debounced Updates** - Debounce rapid updates
336
+
337
+ ```typescript
338
+ // Selective rendering
339
+ const selectedFunnel = useFunnelStore(state => state.selectedFunnel);
340
+
341
+ // Only re-renders when selectedFunnel changes
342
+ ```