@startsimpli/funnels 0.1.4 → 0.1.6

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 -3241
  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 -3194
  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 -20
  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 -18
  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 -389
  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 -386
  151. package/dist/store/index.js.map +0 -1
@@ -0,0 +1,12 @@
1
+ /**
2
+ * ValueInputs Index
3
+ *
4
+ * Centralized exports for all value input components.
5
+ */
6
+
7
+ export { TextValueInput } from './TextValueInput';
8
+ export { NumberValueInput } from './NumberValueInput';
9
+ export { DateValueInput } from './DateValueInput';
10
+ export { BooleanValueInput } from './BooleanValueInput';
11
+ export { ChoiceValueInput } from './ChoiceValueInput';
12
+ export { MultiChoiceValueInput } from './MultiChoiceValueInput';
@@ -0,0 +1,64 @@
1
+ /**
2
+ * FilterRuleEditor Constants
3
+ *
4
+ * Operator labels and field type mappings
5
+ */
6
+
7
+ import { Operator } from '../../types';
8
+
9
+ export const OPERATOR_LABELS: Record<Operator, string> = {
10
+ // Equality
11
+ eq: 'equals',
12
+ ne: 'not equals',
13
+
14
+ // Comparison
15
+ gt: 'greater than',
16
+ lt: 'less than',
17
+ gte: 'greater or equal',
18
+ lte: 'less or equal',
19
+
20
+ // String operations
21
+ contains: 'contains',
22
+ not_contains: 'does not contain',
23
+ startswith: 'starts with',
24
+ endswith: 'ends with',
25
+ matches: 'matches regex',
26
+
27
+ // Array/Set operations
28
+ in: 'is one of',
29
+ not_in: 'is not one of',
30
+ has_any: 'has any of',
31
+ has_all: 'has all of',
32
+
33
+ // Null checks
34
+ isnull: 'is empty',
35
+ isnotnull: 'is not empty',
36
+
37
+ // Tag operations
38
+ has_tag: 'has tag',
39
+ not_has_tag: 'does not have tag',
40
+
41
+ // Boolean
42
+ is_true: 'is true',
43
+ is_false: 'is false',
44
+ };
45
+
46
+ /**
47
+ * Operators that don't require a value input
48
+ */
49
+ export const NULL_VALUE_OPERATORS: Operator[] = [
50
+ 'isnull',
51
+ 'isnotnull',
52
+ 'is_true',
53
+ 'is_false',
54
+ ];
55
+
56
+ /**
57
+ * Operators that require array/multi-value input
58
+ */
59
+ export const MULTI_VALUE_OPERATORS: Operator[] = [
60
+ 'in',
61
+ 'not_in',
62
+ 'has_any',
63
+ 'has_all',
64
+ ];
@@ -0,0 +1,14 @@
1
+ /**
2
+ * FilterRuleEditor Module
3
+ *
4
+ * Exports the main component and sub-components for building filter rules.
5
+ */
6
+
7
+ export { FilterRuleEditor } from './FilterRuleEditor';
8
+ export type { FilterRuleEditorProps } from './FilterRuleEditor';
9
+ export { LogicToggle } from './LogicToggle';
10
+ export { FieldSelector } from './FieldSelector';
11
+ export { OperatorSelector } from './OperatorSelector';
12
+ export { RuleRow } from './RuleRow';
13
+ export * from './ValueInputs';
14
+ export * from './constants';
@@ -0,0 +1,447 @@
1
+ # FunnelCard Design Specification
2
+
3
+ ## Design Goal
4
+
5
+ Users should be able to **quickly assess funnel health, understand its structure, and take action** without cognitive overload.
6
+
7
+ ## User Needs
8
+
9
+ 1. **Identify funnel** - Know which funnel this is (name + status)
10
+ 2. **Understand structure** - See what stages exist and their complexity
11
+ 3. **Assess performance** - Know if funnel is working (match rate)
12
+ 4. **Take action** - Explore details or make changes
13
+
14
+ ## Visual Hierarchy
15
+
16
+ Information is prioritized from most to least important:
17
+
18
+ ```
19
+ ┌─────────────────────────────────────────┐
20
+ │ 1. FUNNEL NAME + STATUS [BADGE] │ ← Primary identification
21
+ │ 2. Description context │ ← Supplementary context
22
+ │────────────────────────────────────────│
23
+ │ 3. ① Stage 1 1 rule │ ← Structure (scannable)
24
+ │ │ │
25
+ │ ② Stage 2 3 rules │
26
+ │ │ │
27
+ │ ③ Stage 3 2 rules │
28
+ │────────────────────────────────────────│
29
+ │ 4. █████████░░░░ 235 matched │ ← Performance (visual)
30
+ │────────────────────────────────────────│
31
+ │ 5. INPUT MATCHED EXCLUDED │ ← Metrics (detailed)
32
+ │────────────────────────────────────────│
33
+ │ 6. [View Flow →] │ ← Action (next step)
34
+ └─────────────────────────────────────────┘
35
+ ```
36
+
37
+ ## Component Breakdown
38
+
39
+ ### 1. StatusBadge
40
+
41
+ **Purpose**: Instant status recognition
42
+
43
+ **Design Rationale**:
44
+ - Pill shape: Soft, friendly aesthetic (vs sharp rectangles)
45
+ - Color coding: Pattern matching > reading text
46
+ - Green (active) = "go" / "working"
47
+ - Yellow (draft) = "caution" / "in progress"
48
+ - Gray (paused) = "neutral" / "stopped"
49
+ - Red (archived) = "stop" / "done"
50
+ - Uppercase text: Increases scannability
51
+ - Small size: De-emphasizes relative to funnel name
52
+ - Right-aligned: Keeps name left-aligned for scanning
53
+
54
+ **Implementation**:
55
+ ```tsx
56
+ <span className="bg-green-100 text-green-800 px-2.5 py-0.5 rounded-full text-xs font-medium">
57
+ ACTIVE
58
+ </span>
59
+ ```
60
+
61
+ ### 2. StageIndicator
62
+
63
+ **Purpose**: Show funnel flow and structure
64
+
65
+ **Design Rationale**:
66
+ - Numbered circles: Clear sequential ordering (①②③④⑤)
67
+ - Unicode circled numbers (U+2460 - U+2473) for 1-20
68
+ - Fallback to (N) for 20+
69
+ - Vertical line: Shows progression/flow between stages
70
+ - Stage name + rule count: Balances detail with brevity
71
+ - Name is primary (font-medium, larger)
72
+ - Rule count is secondary (text-gray-500, smaller)
73
+ - Compact spacing: Many stages fit without scrolling
74
+ - Blue accent: Neutral, distinct from status colors
75
+
76
+ **Flow Visualization**:
77
+ ```
78
+ ① Stage 1 Shows: Order + Name + Complexity
79
+ │ Line: Connects to next stage
80
+ ② Stage 2 Circle: Current stage
81
+ │ Gap: Visual breathing room
82
+ ③ Stage 3 Last stage: No line below
83
+ ```
84
+
85
+ **Implementation**:
86
+ ```tsx
87
+ <div className="flex items-start gap-2">
88
+ <div className="flex flex-col items-center">
89
+ <div className="w-6 h-6 rounded-full bg-blue-100 text-blue-800">①</div>
90
+ {!isLast && <div className="w-0.5 h-6 bg-gray-200" />}
91
+ </div>
92
+ <div className="flex-1">
93
+ <span className="text-sm font-medium">Stage Name</span>
94
+ <span className="text-xs text-gray-500">3 rules</span>
95
+ </div>
96
+ </div>
97
+ ```
98
+
99
+ ### 3. MatchBar
100
+
101
+ **Purpose**: Visual performance indicator
102
+
103
+ **Design Rationale**:
104
+ - Progress bar metaphor: Universal UI pattern
105
+ - Green gradient: Success/completion color
106
+ - Percentage width: Direct visual encoding of match rate
107
+ - Label below: Absolute count (more actionable than %)
108
+ - Gray background: Shows full scale (0-100%)
109
+ - Height (24px): Large enough to see, small enough to scan
110
+
111
+ **Visual States**:
112
+ - 0% matched: Empty gray bar
113
+ - 50% matched: Half-filled green bar
114
+ - 100% matched: Full green bar
115
+
116
+ **Implementation**:
117
+ ```tsx
118
+ <div className="relative h-6 bg-gray-200 rounded-md">
119
+ <div
120
+ className="absolute inset-y-0 left-0 bg-gradient-to-r from-green-500 to-green-600"
121
+ style={{ width: `${percentage}%` }}
122
+ />
123
+ </div>
124
+ <span className="text-sm font-medium">{matched} matched</span>
125
+ ```
126
+
127
+ ### 4. FunnelStats
128
+
129
+ **Purpose**: Detailed performance metrics
130
+
131
+ **Design Rationale**:
132
+ - Three columns: Equal visual weight
133
+ - Color coding reinforces meaning:
134
+ - Blue (INPUT): Neutral starting point
135
+ - Green (MATCHED): Positive outcome
136
+ - Red (EXCLUDED): Filtered out
137
+ - Large numbers: Draw attention to key metrics
138
+ - Small labels: Context without clutter
139
+ - Uppercase labels: Consistency with status badge
140
+ - Background tints: Visual grouping without borders
141
+
142
+ **Layout**:
143
+ ```
144
+ ┌─────────┬─────────┬─────────┐
145
+ │ INPUT │ MATCHED │EXCLUDED │ ← Labels (text-xs)
146
+ │ 83,061 │ 235 │ 82,826 │ ← Values (text-lg, bold)
147
+ └─────────┴─────────┴─────────┘
148
+ Blue Green Red
149
+ ```
150
+
151
+ **Implementation**:
152
+ ```tsx
153
+ <dl className="grid grid-cols-3 gap-2">
154
+ <div className="bg-blue-50 rounded-lg px-3 py-2.5">
155
+ <dt className="text-xs text-gray-600">INPUT</dt>
156
+ <dd className="text-lg font-bold text-blue-600">83,061</dd>
157
+ </div>
158
+ {/* MATCHED and EXCLUDED... */}
159
+ </dl>
160
+ ```
161
+
162
+ ### 5. Action Button
163
+
164
+ **Purpose**: Primary next step
165
+
166
+ **Design Rationale**:
167
+ - Full width: Maximum touch target
168
+ - Gray background: De-emphasizes vs primary blue (not destructive action)
169
+ - Arrow icon: Indicates forward movement
170
+ - Hover state: Background darkens
171
+ - Focus state: Blue ring for keyboard navigation
172
+ - Text + icon: Balanced composition
173
+
174
+ **States**:
175
+ - Default: bg-gray-50
176
+ - Hover: bg-gray-100
177
+ - Focus: ring-2 ring-blue-500
178
+ - Active: bg-gray-200 (pressed)
179
+
180
+ ## Layout Architecture
181
+
182
+ ### Card Container
183
+
184
+ ```
185
+ ┌─────────────────────────────────────┐
186
+ │ px-6 pt-5 pb-3 HEADER │ ← 24px horizontal padding
187
+ ├─────────────────────────────────────┤ 20px top, 12px bottom
188
+ │ px-6 py-4 STAGES │ ← 24px horizontal, 16px vertical
189
+ ├─────────────────────────────────────┤
190
+ │ px-6 py-4 MATCH BAR │
191
+ ├─────────────────────────────────────┤
192
+ │ px-6 py-4 STATS │
193
+ ├─────────────────────────────────────┤
194
+ │ px-6 py-4 ACTION │
195
+ └─────────────────────────────────────┘
196
+ ```
197
+
198
+ ### Spacing System
199
+
200
+ - **Card padding**: 24px (px-6) horizontal, consistent throughout
201
+ - **Section padding**: 16px (py-4) vertical between sections
202
+ - **Border**: 1px solid gray-200 between sections
203
+ - **Card border**: 1px solid gray-200 around entire card
204
+ - **Card shadow**: shadow-sm (subtle), shadow-md on hover
205
+ - **Border radius**: rounded-lg (8px) for card, rounded-md (6px) for elements
206
+
207
+ ### Grid Layout (Stats)
208
+
209
+ ```
210
+ grid-cols-3 Equal columns
211
+ gap-2 8px gap between columns
212
+ px-3 py-2.5 12px horizontal, 10px vertical padding per stat
213
+ ```
214
+
215
+ ## Color Palette
216
+
217
+ ### Status Colors
218
+ ```
219
+ Active: bg-green-100 (#dcfce7) text-green-800 (#166534)
220
+ Draft: bg-yellow-100 (#fef9c3) text-yellow-800 (#854d0e)
221
+ Paused: bg-gray-100 (#f3f4f6) text-gray-800 (#1f2937)
222
+ Archived: bg-red-100 (#fee2e2) text-red-800 (#991b1b)
223
+ ```
224
+
225
+ ### Stats Colors
226
+ ```
227
+ INPUT: bg-blue-50 (#eff6ff) text-blue-600 (#2563eb)
228
+ MATCHED: bg-green-50 (#f0fdf4) text-green-600 (#16a34a)
229
+ EXCLUDED: bg-red-50 (#fef2f2) text-red-600 (#dc2626)
230
+ ```
231
+
232
+ ### Structural Colors
233
+ ```
234
+ Card bg: white (#ffffff)
235
+ Card border: gray-200 (#e5e7eb)
236
+ Section border: gray-100 (#f3f4f6)
237
+ Text primary: gray-900 (#111827)
238
+ Text secondary: gray-600 (#4b5563)
239
+ Text tertiary: gray-500 (#6b7280)
240
+ Match bar bg: gray-200 (#e5e7eb)
241
+ Match bar fill: gradient green-500 → green-600
242
+ Button bg: gray-50 (#f9fafb)
243
+ Button hover: gray-100 (#f3f4f6)
244
+ ```
245
+
246
+ ## Typography Scale
247
+
248
+ ```
249
+ Funnel name: text-lg (18px) font-semibold line-height-tight
250
+ Description: text-sm (14px) font-normal line-height-relaxed
251
+ Stage name: text-sm (14px) font-medium line-height-normal
252
+ Rule count: text-xs (12px) font-normal line-height-normal
253
+ Stats label: text-xs (12px) font-medium line-height-normal
254
+ Stats value: text-lg (18px) font-bold line-height-tight
255
+ Match label: text-sm (14px) font-medium line-height-normal
256
+ Button text: text-sm (14px) font-medium line-height-normal
257
+ Status badge: text-xs (12px) font-medium line-height-normal
258
+ ```
259
+
260
+ ## Responsive Breakpoints
261
+
262
+ ### Mobile (< 640px)
263
+ - Full width cards
264
+ - Same vertical layout (no changes needed)
265
+ - Touch-friendly 44px minimum button height
266
+
267
+ ### Tablet (640px - 1024px)
268
+ - Constrained card width (max-w-2xl suggested for container)
269
+ - Same vertical layout
270
+
271
+ ### Desktop (> 1024px)
272
+ - Max card width (max-w-3xl suggested for container)
273
+ - Consider grid layout for multiple cards (grid-cols-2 or grid-cols-3)
274
+
275
+ ## Accessibility Specifications
276
+
277
+ ### Color Contrast (WCAG AA 4.5:1)
278
+
279
+ **Status Badges**:
280
+ - Green: 5.2:1 ✓
281
+ - Yellow: 7.1:1 ✓
282
+ - Gray: 10.4:1 ✓
283
+ - Red: 6.8:1 ✓
284
+
285
+ **Stats**:
286
+ - Blue: 5.4:1 ✓
287
+ - Green: 5.1:1 ✓
288
+ - Red: 5.2:1 ✓
289
+
290
+ **Text**:
291
+ - Primary (gray-900): 16.1:1 ✓
292
+ - Secondary (gray-600): 7.2:1 ✓
293
+ - Tertiary (gray-500): 4.6:1 ✓
294
+
295
+ ### Semantic HTML
296
+
297
+ ```html
298
+ <article aria-label="Funnel: {name}">
299
+ <header>
300
+ <h3>Funnel Name</h3>
301
+ <StatusBadge />
302
+ <p>Description</p>
303
+ </header>
304
+
305
+ <section aria-label="Funnel stages">
306
+ <StageIndicator />
307
+ </section>
308
+
309
+ <section aria-label="Match results">
310
+ <MatchBar role="progressbar" aria-valuemin="0" aria-valuemax="100" />
311
+ </section>
312
+
313
+ <section aria-label="Funnel statistics">
314
+ <dl>
315
+ <dt>INPUT</dt>
316
+ <dd>83,061</dd>
317
+ </dl>
318
+ </section>
319
+
320
+ <footer>
321
+ <button aria-label="View flow details for {name}">
322
+ View Flow →
323
+ </button>
324
+ </footer>
325
+ </article>
326
+ ```
327
+
328
+ ### Keyboard Navigation
329
+
330
+ - **Tab**: Focus on button (only interactive element)
331
+ - **Enter/Space**: Trigger button action
332
+ - **Focus visible**: Blue ring (ring-2 ring-blue-500 ring-offset-2)
333
+
334
+ ## Interaction States
335
+
336
+ ### Card
337
+ - **Default**: shadow-sm
338
+ - **Hover**: shadow-md (elevation increase)
339
+ - **Transition**: 200ms ease-in-out
340
+
341
+ ### Button
342
+ - **Default**: bg-gray-50 text-gray-900
343
+ - **Hover**: bg-gray-100 (arrow shifts right 2px)
344
+ - **Focus**: ring-2 ring-blue-500 ring-offset-2
345
+ - **Active**: bg-gray-200
346
+ - **Disabled**: bg-gray-50 text-gray-400 cursor-not-allowed opacity-60
347
+
348
+ ### Match Bar
349
+ - **Default**: Static
350
+ - **Update**: 300ms width transition (smooth)
351
+
352
+ ## Empty States
353
+
354
+ ### No Runs Yet
355
+ ```
356
+ ┌─────────────────────────────────────┐
357
+ │ Funnel Name [ACTIVE] │
358
+ │ Description... │
359
+ ├─────────────────────────────────────┤
360
+ │ ① Stage 1 1 rule │
361
+ │ │ │
362
+ │ ② Stage 2 3 rules │
363
+ ├─────────────────────────────────────┤
364
+ │ No runs yet │ ← Centered, gray-500
365
+ ├─────────────────────────────────────┤
366
+ │ [View Flow →] │
367
+ └─────────────────────────────────────┘
368
+ ```
369
+
370
+ ### No Stages
371
+ ```
372
+ ┌─────────────────────────────────────┐
373
+ │ Funnel Name [DRAFT] │
374
+ │ Description... │
375
+ ├─────────────────────────────────────┤
376
+ │ No stages defined │ ← Centered, gray-500, italic
377
+ ├─────────────────────────────────────┤
378
+ │ [View Flow →] │
379
+ └─────────────────────────────────────┘
380
+ ```
381
+
382
+ ### Failed Run
383
+ ```
384
+ ┌─────────────────────────────────────┐
385
+ │ Funnel Name [ACTIVE] │
386
+ │ Description... │
387
+ ├─────────────────────────────────────┤
388
+ │ ① Stage 1 1 rule │
389
+ ├─────────────────────────────────────┤
390
+ │ Last run failed │ ← Centered, gray-500
391
+ ├─────────────────────────────────────┤
392
+ │ [View Flow →] │
393
+ └─────────────────────────────────────┘
394
+ ```
395
+
396
+ ## Performance Considerations
397
+
398
+ ### Rendering
399
+ - Pure CSS (no images)
400
+ - SVG arrow icon (inline, no network request)
401
+ - Minimal re-renders (React.memo candidate)
402
+ - No expensive calculations (percentage is simple math)
403
+
404
+ ### Loading States
405
+ - Skeleton screens (optional future enhancement)
406
+ - Progressive disclosure (stats only shown when run exists)
407
+
408
+ ### Animation Budget
409
+ - Card hover: transform translateY (GPU accelerated)
410
+ - Match bar: width transition (layout, but acceptable)
411
+ - Button hover: background color (paint only)
412
+
413
+ ## Implementation Notes
414
+
415
+ ### Tailwind CSS Classes
416
+
417
+ All components use **utility-first** Tailwind classes:
418
+ - No custom CSS files
419
+ - All styling inline via className
420
+ - Easy to maintain and understand
421
+ - Consistent with design system
422
+
423
+ ### Type Safety
424
+
425
+ All components are **fully typed**:
426
+ - Props interfaces exported
427
+ - Type guards available (isFunnel, isFunnelRun)
428
+ - Generic over entity type (`Funnel<TEntity>`)
429
+
430
+ ### Testing
431
+
432
+ Component is **fully tested**:
433
+ - Visual regression (Storybook)
434
+ - Unit tests (Vitest + Testing Library)
435
+ - Accessibility tests (ARIA attributes)
436
+ - Edge cases (empty states, long text, etc.)
437
+
438
+ ## Future Enhancements
439
+
440
+ 1. **Edit Mode**: Click header to edit funnel name/description
441
+ 2. **Stage Expansion**: Click stage to see rule details
442
+ 3. **Run History**: Dropdown showing past runs
443
+ 4. **Comparison View**: Compare multiple runs
444
+ 5. **Export**: Download funnel configuration as JSON
445
+ 6. **Clone**: Duplicate funnel with one click
446
+ 7. **Archive Confirmation**: Modal before archiving
447
+ 8. **Drag to Reorder**: Reorder stages within card