@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,304 @@
1
+ # FunnelCard Installation Guide
2
+
3
+ ## Quick Start
4
+
5
+ The FunnelCard component is already built and ready to use. It just needs React dependencies to render.
6
+
7
+ ## Install React Dependencies
8
+
9
+ Since this package currently only has TypeScript/Zustand dependencies, you'll need to add React:
10
+
11
+ ```bash
12
+ cd /Users/qosha/Repos/small-bizs/startsimpli/start-simpli-api/packages/funnels
13
+
14
+ # Add React (peer dependency)
15
+ npm install --save-peer react react-dom
16
+
17
+ # Add React types (dev dependency)
18
+ npm install --save-dev @types/react @types/react-dom
19
+ ```
20
+
21
+ ## Optional: Testing Dependencies
22
+
23
+ To run the React component tests (`FunnelCard.test.tsx`):
24
+
25
+ ```bash
26
+ # Install testing libraries
27
+ npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event
28
+ ```
29
+
30
+ ## Optional: Storybook
31
+
32
+ To view the Storybook stories (`FunnelCard.stories.tsx`):
33
+
34
+ ```bash
35
+ # Initialize Storybook
36
+ npx storybook@latest init
37
+
38
+ # Start Storybook
39
+ npm run storybook
40
+ ```
41
+
42
+ ## Tailwind CSS Setup
43
+
44
+ FunnelCard uses Tailwind CSS classes. If your project doesn't have Tailwind:
45
+
46
+ ### 1. Install Tailwind
47
+
48
+ ```bash
49
+ npm install --save-dev tailwindcss postcss autoprefixer
50
+ npx tailwindcss init -p
51
+ ```
52
+
53
+ ### 2. Configure Tailwind
54
+
55
+ **tailwind.config.js**:
56
+ ```javascript
57
+ /** @type {import('tailwindcss').Config} */
58
+ module.exports = {
59
+ content: [
60
+ './src/**/*.{ts,tsx}',
61
+ ],
62
+ theme: {
63
+ extend: {},
64
+ },
65
+ plugins: [],
66
+ }
67
+ ```
68
+
69
+ ### 3. Add Tailwind Directives
70
+
71
+ Create `src/styles.css`:
72
+ ```css
73
+ @tailwind base;
74
+ @tailwind components;
75
+ @tailwind utilities;
76
+ ```
77
+
78
+ Import in your entry file:
79
+ ```typescript
80
+ import './styles.css';
81
+ ```
82
+
83
+ ## Verify Installation
84
+
85
+ Test that components compile:
86
+
87
+ ```bash
88
+ # Type check
89
+ npm run type-check
90
+
91
+ # Run tests (data validation tests work without React)
92
+ npm test src/components/FunnelCard/FunnelCard.test.ts
93
+ ```
94
+
95
+ ## Usage in Your Project
96
+
97
+ ### Import Components
98
+
99
+ ```typescript
100
+ import { FunnelCard } from '@simpli/funnels';
101
+
102
+ // Or import sub-components directly
103
+ import { StatusBadge, StageIndicator, MatchBar, FunnelStats } from '@simpli/funnels';
104
+ ```
105
+
106
+ ### Basic Example
107
+
108
+ ```tsx
109
+ import { FunnelCard } from '@simpli/funnels';
110
+
111
+ function MyComponent() {
112
+ const funnel = {
113
+ id: 'funnel-1',
114
+ name: 'Test Funnel',
115
+ status: 'active',
116
+ input_type: 'any',
117
+ stages: [
118
+ {
119
+ id: 'stage-1',
120
+ order: 0,
121
+ name: 'Stage 1',
122
+ filter_logic: 'AND',
123
+ rules: [{ field_path: 'name', operator: 'eq', value: 'test' }],
124
+ match_action: 'continue',
125
+ no_match_action: 'exclude',
126
+ },
127
+ ],
128
+ created_at: new Date().toISOString(),
129
+ updated_at: new Date().toISOString(),
130
+ };
131
+
132
+ const latestRun = {
133
+ id: 'run-1',
134
+ funnel_id: 'funnel-1',
135
+ status: 'completed',
136
+ trigger_type: 'manual',
137
+ started_at: new Date().toISOString(),
138
+ total_input: 1000,
139
+ total_matched: 50,
140
+ total_excluded: 950,
141
+ total_tagged: 50,
142
+ stage_stats: {},
143
+ };
144
+
145
+ return (
146
+ <FunnelCard
147
+ funnel={funnel}
148
+ latestRun={latestRun}
149
+ onViewFlow={(f) => console.log('View:', f.id)}
150
+ />
151
+ );
152
+ }
153
+ ```
154
+
155
+ ## Integration with Next.js
156
+
157
+ If using in a Next.js app:
158
+
159
+ ### 1. Mark as Client Component
160
+
161
+ ```tsx
162
+ 'use client';
163
+
164
+ import { FunnelCard } from '@simpli/funnels';
165
+
166
+ export default function FunnelsPage() {
167
+ // ...
168
+ }
169
+ ```
170
+
171
+ ### 2. Import Tailwind Styles
172
+
173
+ In `app/layout.tsx` or `_app.tsx`:
174
+ ```tsx
175
+ import '@simpli/funnels/dist/styles.css'; // If package exports styles
176
+ // OR import Tailwind directly:
177
+ import 'tailwindcss/tailwind.css';
178
+ ```
179
+
180
+ ## Integration with Django Backend
181
+
182
+ If using with Django API:
183
+
184
+ ### 1. Fetch Funnel Data
185
+
186
+ ```typescript
187
+ async function fetchFunnels() {
188
+ const response = await fetch('/api/funnels/', {
189
+ headers: {
190
+ 'Authorization': `Bearer ${token}`,
191
+ },
192
+ });
193
+ return response.json();
194
+ }
195
+ ```
196
+
197
+ ### 2. Render Cards
198
+
199
+ ```tsx
200
+ function FunnelDashboard() {
201
+ const [funnels, setFunnels] = useState([]);
202
+
203
+ useEffect(() => {
204
+ fetchFunnels().then(setFunnels);
205
+ }, []);
206
+
207
+ return (
208
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
209
+ {funnels.map(funnel => (
210
+ <FunnelCard
211
+ key={funnel.id}
212
+ funnel={funnel}
213
+ latestRun={funnel.latest_run}
214
+ onViewFlow={(f) => router.push(`/funnels/${f.id}`)}
215
+ />
216
+ ))}
217
+ </div>
218
+ );
219
+ }
220
+ ```
221
+
222
+ ## Troubleshooting
223
+
224
+ ### Issue: "Cannot find module 'react'"
225
+
226
+ **Solution**: Install React dependencies
227
+ ```bash
228
+ npm install react react-dom @types/react @types/react-dom
229
+ ```
230
+
231
+ ### Issue: "Unknown at rule @tailwind"
232
+
233
+ **Solution**: Install and configure Tailwind CSS (see above)
234
+
235
+ ### Issue: Tests failing with "ReferenceError: React is not defined"
236
+
237
+ **Solution**: Import React in test file
238
+ ```typescript
239
+ import React from 'react';
240
+ import { render } from '@testing-library/react';
241
+ ```
242
+
243
+ ### Issue: Styles not applying
244
+
245
+ **Solution**: Ensure Tailwind is configured and CSS is imported
246
+ ```typescript
247
+ import 'tailwindcss/tailwind.css';
248
+ ```
249
+
250
+ ### Issue: Type errors in Storybook
251
+
252
+ **Solution**: Install Storybook types
253
+ ```bash
254
+ npm install --save-dev @storybook/react @storybook/addon-essentials
255
+ ```
256
+
257
+ ## Production Build
258
+
259
+ When building for production:
260
+
261
+ ```bash
262
+ # Build package
263
+ npm run build
264
+
265
+ # Or if using with bundler (webpack, vite, etc.)
266
+ # Components will be tree-shaken automatically
267
+ ```
268
+
269
+ ## CDN Usage (Not Recommended)
270
+
271
+ For quick prototyping only:
272
+
273
+ ```html
274
+ <!-- React -->
275
+ <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
276
+ <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
277
+
278
+ <!-- Tailwind CSS -->
279
+ <script src="https://cdn.tailwindcss.com"></script>
280
+
281
+ <!-- Your bundled package -->
282
+ <script src="/dist/funnels.umd.js"></script>
283
+ ```
284
+
285
+ Use npm install for production.
286
+
287
+ ## Next Steps
288
+
289
+ 1. ✓ Install React dependencies
290
+ 2. ✓ Configure Tailwind CSS
291
+ 3. ✓ Import FunnelCard component
292
+ 4. ✓ Pass funnel and latestRun props
293
+ 5. ✓ Handle onViewFlow callback
294
+ 6. View README.md for detailed usage
295
+ 7. View DESIGN.md for design specifications
296
+ 8. Check out Storybook for visual examples
297
+
298
+ ## Questions?
299
+
300
+ See other documentation files:
301
+ - `README.md` - Component usage guide
302
+ - `DESIGN.md` - Design specifications and rationale
303
+ - `IMPLEMENTATION_SUMMARY.md` - Complete implementation details
304
+ - `FunnelCard.tsx` - Source code with inline comments
@@ -0,0 +1,49 @@
1
+ /**
2
+ * MatchBar Component
3
+ *
4
+ * Progress bar showing match percentage with count label.
5
+ *
6
+ * Design Rationale:
7
+ * - Green gradient conveys success/completion
8
+ * - Rounded corners match card aesthetic
9
+ * - Label shows absolute count (more actionable than percentage alone)
10
+ * - Gray background shows full scale
11
+ *
12
+ * Accessibility:
13
+ * - Text label provides non-visual indication of value
14
+ * - Sufficient contrast between bar and background
15
+ */
16
+
17
+ interface MatchBarProps {
18
+ matched: number;
19
+ total: number;
20
+ className?: string;
21
+ }
22
+
23
+ export function MatchBar({ matched, total, className = '' }: MatchBarProps) {
24
+ const percentage = total > 0 ? Math.round((matched / total) * 100) : 0;
25
+
26
+ return (
27
+ <div className={`space-y-1 ${className}`}>
28
+ {/* Progress bar */}
29
+ <div className="relative h-6 bg-gray-200 rounded-md overflow-hidden">
30
+ <div
31
+ className="absolute inset-y-0 left-0 bg-gradient-to-r from-green-500 to-green-600 transition-all duration-300"
32
+ style={{ width: `${percentage}%` }}
33
+ role="progressbar"
34
+ aria-valuenow={percentage}
35
+ aria-valuemin={0}
36
+ aria-valuemax={100}
37
+ aria-label={`${matched} of ${total} matched`}
38
+ />
39
+ </div>
40
+
41
+ {/* Match count label */}
42
+ <div className="text-right">
43
+ <span className="text-sm font-medium text-gray-700">
44
+ {matched.toLocaleString()} matched
45
+ </span>
46
+ </div>
47
+ </div>
48
+ );
49
+ }
@@ -0,0 +1,294 @@
1
+ # FunnelCard Component
2
+
3
+ BRUTALLY GENERIC funnel card component matching Django admin screenshot design.
4
+
5
+ ## Overview
6
+
7
+ FunnelCard displays a funnel overview with:
8
+ - **Header**: Funnel name + color-coded status badge
9
+ - **Description**: Optional descriptive text
10
+ - **Stage List**: Sequential stages with numbered indicators and rule counts
11
+ - **Match Bar**: Visual progress bar showing match percentage
12
+ - **Stats**: Three-column metrics (INPUT / MATCHED / EXCLUDED)
13
+ - **Action**: "View Flow" button
14
+
15
+ ## Usage
16
+
17
+ ```tsx
18
+ import { FunnelCard } from '@simpli/funnels';
19
+
20
+ function MyComponent() {
21
+ const funnel = {
22
+ id: 'funnel-1',
23
+ name: 'Series A Investor Qualification',
24
+ description: 'Identifies qualified Series A investors',
25
+ status: 'active',
26
+ input_type: 'contacts',
27
+ stages: [
28
+ {
29
+ id: 'stage-1',
30
+ order: 0,
31
+ name: 'Stage Filter',
32
+ filter_logic: 'AND',
33
+ rules: [
34
+ { field_path: 'firm.stage', operator: 'in', value: ['Series A'] }
35
+ ],
36
+ match_action: 'continue',
37
+ no_match_action: 'exclude',
38
+ },
39
+ // ... more stages
40
+ ],
41
+ created_at: '2024-01-01T00:00:00Z',
42
+ updated_at: '2024-01-01T00:00:00Z',
43
+ };
44
+
45
+ const latestRun = {
46
+ id: 'run-1',
47
+ funnel_id: 'funnel-1',
48
+ status: 'completed',
49
+ trigger_type: 'manual',
50
+ started_at: '2024-01-01T00:00:00Z',
51
+ completed_at: '2024-01-01T00:01:00Z',
52
+ total_input: 83061,
53
+ total_matched: 235,
54
+ total_excluded: 82826,
55
+ total_tagged: 235,
56
+ stage_stats: {},
57
+ };
58
+
59
+ return (
60
+ <FunnelCard
61
+ funnel={funnel}
62
+ latestRun={latestRun}
63
+ onViewFlow={(funnel) => console.log('View:', funnel.id)}
64
+ />
65
+ );
66
+ }
67
+ ```
68
+
69
+ ## Props
70
+
71
+ | Prop | Type | Required | Description |
72
+ |------|------|----------|-------------|
73
+ | `funnel` | `Funnel` | Yes | Funnel definition (BRUTALLY GENERIC) |
74
+ | `latestRun` | `FunnelRun` | No | Latest run results. Shows "no runs yet" if missing |
75
+ | `onViewFlow` | `(funnel: Funnel) => void` | No | Callback when "View Flow" button clicked |
76
+ | `onEdit` | `(funnel: Funnel) => void` | No | Callback for edit action (future use) |
77
+ | `className` | `string` | No | Additional CSS classes |
78
+
79
+ ## Visual States
80
+
81
+ ### Status Badges
82
+ - **ACTIVE** - Green badge (bg-green-100, text-green-800)
83
+ - **DRAFT** - Yellow badge (bg-yellow-100, text-yellow-800)
84
+ - **PAUSED** - Gray badge (bg-gray-100, text-gray-800)
85
+ - **ARCHIVED** - Red badge (bg-red-100, text-red-800)
86
+
87
+ ### Run States
88
+ - **Completed** - Shows match bar + stats
89
+ - **Running** - Shows "Running..." message
90
+ - **Failed** - Shows "Last run failed" message
91
+ - **No runs** - Shows "No runs yet" message
92
+
93
+ ### Empty States
94
+ - **No stages** - Shows "No stages defined" in stage section
95
+ - **No description** - Description section omitted
96
+ - **Zero matches** - Match bar at 0%, stats show zeros
97
+
98
+ ## Sub-Components
99
+
100
+ All sub-components are exported for composition flexibility:
101
+
102
+ ### StatusBadge
103
+ ```tsx
104
+ import { StatusBadge } from '@simpli/funnels';
105
+
106
+ <StatusBadge status="active" />
107
+ ```
108
+
109
+ ### StageIndicator
110
+ ```tsx
111
+ import { StageIndicator } from '@simpli/funnels';
112
+
113
+ <StageIndicator
114
+ order={0}
115
+ name="Stage Filter"
116
+ ruleCount={3}
117
+ isLast={false}
118
+ />
119
+ ```
120
+
121
+ ### MatchBar
122
+ ```tsx
123
+ import { MatchBar } from '@simpli/funnels';
124
+
125
+ <MatchBar matched={235} total={83061} />
126
+ ```
127
+
128
+ ### FunnelStats
129
+ ```tsx
130
+ import { FunnelStats } from '@simpli/funnels';
131
+
132
+ <FunnelStats input={83061} matched={235} excluded={82826} />
133
+ ```
134
+
135
+ ## Design Specifications
136
+
137
+ ### Layout
138
+ ```
139
+ ┌─────────────────────────────────────────┐
140
+ │ Funnel Name [ACTIVE badge] │ Header
141
+ │ Description text here... │
142
+ │ │
143
+ │ ① Stage 1 Name 1 rule │ Stages
144
+ │ │ │
145
+ │ ② Stage 2 Name 3 rules │
146
+ │ │ │
147
+ │ ③ Stage 3 Name 2 rules │
148
+ │ │
149
+ │ ████████████░░░░░░░░ 235 matched │ Match Bar
150
+ │ │
151
+ │ 83061 INPUT 235 MATCHED 82826 EXCLUDED │ Stats
152
+ │ │
153
+ │ [View Flow →] │ Action
154
+ └─────────────────────────────────────────┘
155
+ ```
156
+
157
+ ### Spacing
158
+ - Card padding: 24px (px-6)
159
+ - Section spacing: 16px (py-4)
160
+ - Stage gap: 0px (vertical line connects)
161
+ - Stats grid gap: 8px (gap-2)
162
+
163
+ ### Colors
164
+ - Card: white background, gray-200 border, shadow-sm
165
+ - Status Active: green-100 bg, green-800 text
166
+ - Status Draft: yellow-100 bg, yellow-800 text
167
+ - Status Paused: gray-100 bg, gray-800 text
168
+ - Status Archived: red-100 bg, red-800 text
169
+ - Match Bar: green-500 to green-600 gradient
170
+ - Stats INPUT: blue-50 bg, blue-600 text
171
+ - Stats MATCHED: green-50 bg, green-600 text
172
+ - Stats EXCLUDED: red-50 bg, red-600 text
173
+
174
+ ### Typography
175
+ - Funnel name: text-lg, font-semibold
176
+ - Description: text-sm, text-gray-600
177
+ - Stage name: text-sm, font-medium
178
+ - Rule count: text-xs, text-gray-500
179
+ - Stats label: text-xs, font-medium
180
+ - Stats value: text-lg, font-bold
181
+ - Button: text-sm, font-medium
182
+
183
+ ### Responsive Behavior
184
+ - Mobile (< 640px): Full width, single column
185
+ - Tablet (640px - 1024px): Constrained width
186
+ - Desktop (> 1024px): Max width, centered
187
+
188
+ ## Accessibility
189
+
190
+ ### Semantic HTML
191
+ - `<article>` for card container
192
+ - `<header>` for funnel name + status
193
+ - `<section>` for each major area
194
+ - `<footer>` for action button
195
+ - `<dl>/<dt>/<dd>` for stats
196
+
197
+ ### ARIA Attributes
198
+ - `aria-label` on article: "Funnel: {name}"
199
+ - `aria-label` on sections: "Funnel stages", "Match results", etc.
200
+ - `aria-label` on button: "View flow details for {name}"
201
+ - `role="progressbar"` on match bar with aria-value* attributes
202
+
203
+ ### Keyboard Navigation
204
+ - Button is fully keyboard accessible
205
+ - Focus visible with ring-2 ring-blue-500
206
+ - Tab order follows visual flow
207
+
208
+ ### Color Contrast
209
+ - All text meets WCAG AA 4.5:1 minimum
210
+ - Status badges: 4.5:1 contrast
211
+ - Stats: 4.5:1 contrast
212
+ - Button: 4.5:1 contrast
213
+
214
+ ## Examples
215
+
216
+ ### Investor Funnel
217
+ ```tsx
218
+ <FunnelCard
219
+ funnel={{
220
+ name: 'Series A Investor Qualification',
221
+ status: 'active',
222
+ stages: [/* ... */],
223
+ // ...
224
+ }}
225
+ latestRun={{
226
+ total_input: 83061,
227
+ total_matched: 235,
228
+ total_excluded: 82826,
229
+ // ...
230
+ }}
231
+ />
232
+ ```
233
+
234
+ ### Recipe Funnel
235
+ ```tsx
236
+ <FunnelCard
237
+ funnel={{
238
+ name: 'Quick Easy Dinner Recommendations',
239
+ status: 'active',
240
+ stages: [/* ... */],
241
+ // ...
242
+ }}
243
+ latestRun={{
244
+ total_input: 5420,
245
+ total_matched: 87,
246
+ total_excluded: 5333,
247
+ // ...
248
+ }}
249
+ />
250
+ ```
251
+
252
+ ### Lead Scoring Funnel
253
+ ```tsx
254
+ <FunnelCard
255
+ funnel={{
256
+ name: 'Enterprise Lead Scoring',
257
+ status: 'active',
258
+ stages: [/* ... */],
259
+ // ...
260
+ }}
261
+ latestRun={{
262
+ total_input: 12500,
263
+ total_matched: 342,
264
+ total_excluded: 12158,
265
+ // ...
266
+ }}
267
+ />
268
+ ```
269
+
270
+ ## Testing
271
+
272
+ Run tests:
273
+ ```bash
274
+ npm test src/components/FunnelCard/FunnelCard.test.tsx
275
+ ```
276
+
277
+ View stories:
278
+ ```bash
279
+ npm run storybook
280
+ ```
281
+
282
+ ## Dependencies
283
+
284
+ - React (peer dependency)
285
+ - Tailwind CSS (styling)
286
+ - TypeScript (type safety)
287
+
288
+ ## Browser Support
289
+
290
+ - Chrome/Edge: Latest 2 versions
291
+ - Firefox: Latest 2 versions
292
+ - Safari: Latest 2 versions
293
+ - Mobile Safari: Latest 2 versions
294
+ - Chrome Android: Latest 2 versions
@@ -0,0 +1,62 @@
1
+ /**
2
+ * StageIndicator Component
3
+ *
4
+ * Displays a numbered circle badge for funnel stages with connecting vertical line.
5
+ *
6
+ * Design Rationale:
7
+ * - Numbered circles (①②③) provide clear sequential ordering
8
+ * - Vertical connecting line shows flow/progression
9
+ * - Compact layout conserves vertical space
10
+ * - Gray text for rule count de-emphasizes secondary info
11
+ *
12
+ * Accessibility:
13
+ * - Sufficient color contrast (4.5:1 minimum)
14
+ * - Semantic HTML structure
15
+ */
16
+
17
+ interface StageIndicatorProps {
18
+ order: number;
19
+ name: string;
20
+ ruleCount: number;
21
+ isLast?: boolean;
22
+ className?: string;
23
+ }
24
+
25
+ export function StageIndicator({
26
+ order,
27
+ name,
28
+ ruleCount,
29
+ isLast = false,
30
+ className = ''
31
+ }: StageIndicatorProps) {
32
+ // Format order as circled number (1-20 supported by Unicode)
33
+ const circledNumber = order < 20
34
+ ? String.fromCharCode(9312 + order) // ① = U+2460
35
+ : `(${order + 1})`;
36
+
37
+ return (
38
+ <div className={`flex items-start gap-2 ${className}`}>
39
+ {/* Stage number circle with connecting line */}
40
+ <div className="flex flex-col items-center">
41
+ <div className="flex-shrink-0 w-6 h-6 rounded-full bg-blue-100 text-blue-800 flex items-center justify-center text-sm font-medium">
42
+ {circledNumber}
43
+ </div>
44
+ {!isLast && (
45
+ <div className="w-0.5 h-6 bg-gray-200 mt-1" />
46
+ )}
47
+ </div>
48
+
49
+ {/* Stage name and rule count */}
50
+ <div className="flex-1 pt-0.5 min-w-0">
51
+ <div className="flex items-baseline justify-between gap-2">
52
+ <span className="text-sm font-medium text-gray-900 truncate">
53
+ {name}
54
+ </span>
55
+ <span className="text-xs text-gray-500 whitespace-nowrap">
56
+ {ruleCount} {ruleCount === 1 ? 'rule' : 'rules'}
57
+ </span>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ );
62
+ }