@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,204 @@
1
+ /**
2
+ * FunnelCard Component
3
+ *
4
+ * Main card component displaying funnel overview with stages, match stats, and actions.
5
+ *
6
+ * Design Goal:
7
+ * Users should be able to quickly assess funnel health, understand its structure,
8
+ * and take action (view details) without cognitive overload.
9
+ *
10
+ * Visual Hierarchy:
11
+ * 1. Funnel name + status (primary identification)
12
+ * 2. Description (context)
13
+ * 3. Stage flow (structure understanding)
14
+ * 4. Match bar (visual performance indicator)
15
+ * 5. Stats (detailed performance metrics)
16
+ * 6. Action button (next step)
17
+ *
18
+ * Layout: Card with white background, shadow, rounded corners (matches Django admin aesthetic)
19
+ *
20
+ * Responsive Behavior:
21
+ * - Mobile (< 640px): Full width, stack all elements
22
+ * - Tablet (640px - 1024px): Constrained width, same vertical layout
23
+ * - Desktop (> 1024px): Max width with horizontal centering possible
24
+ *
25
+ * Accessibility:
26
+ * - Semantic HTML (article, header, section, footer)
27
+ * - ARIA labels for interactive elements
28
+ * - Keyboard navigation support
29
+ * - Color contrast meets WCAG AA (4.5:1)
30
+ * - Focus indicators on interactive elements
31
+ *
32
+ * Interaction Notes:
33
+ * - Card hover: subtle elevation increase (shadow deepens)
34
+ * - Button hover: background darkens, arrow shifts right
35
+ * - Button focus: ring outline for keyboard navigation
36
+ * - Click zones: entire button is clickable, not just text
37
+ *
38
+ * Performance:
39
+ * - No images (icon-free design)
40
+ * - CSS-only animations
41
+ * - Efficient re-renders (React.memo potential)
42
+ */
43
+
44
+ import { Funnel, FunnelRun } from '../../types';
45
+ import { StatusBadge } from './StatusBadge';
46
+ import { StageIndicator } from './StageIndicator';
47
+ import { MatchBar } from './MatchBar';
48
+ import { FunnelStats } from './FunnelStats';
49
+
50
+ export interface FunnelCardProps {
51
+ /** Funnel definition (BRUTALLY GENERIC - works for any entity type) */
52
+ funnel: Funnel;
53
+
54
+ /** Latest run results (optional - shows "no runs yet" state if missing) */
55
+ latestRun?: FunnelRun;
56
+
57
+ /** Callback when "View Flow" is clicked */
58
+ onViewFlow?: (funnel: Funnel) => void;
59
+
60
+ /** Callback when edit action is triggered (future: click on card header) */
61
+ onEdit?: (funnel: Funnel) => void;
62
+
63
+ /** Additional CSS classes */
64
+ className?: string;
65
+ }
66
+
67
+ export function FunnelCard({
68
+ funnel,
69
+ latestRun,
70
+ onViewFlow,
71
+ onEdit,
72
+ className = ''
73
+ }: FunnelCardProps) {
74
+ // Extract stats from latest run or use zeros
75
+ const stats = latestRun ? {
76
+ input: latestRun.total_input,
77
+ matched: latestRun.total_matched,
78
+ excluded: latestRun.total_excluded,
79
+ } : {
80
+ input: 0,
81
+ matched: 0,
82
+ excluded: 0,
83
+ };
84
+
85
+ const handleViewFlow = () => {
86
+ if (onViewFlow) {
87
+ onViewFlow(funnel);
88
+ }
89
+ };
90
+
91
+ const hasRun = latestRun && latestRun.status === 'completed';
92
+
93
+ return (
94
+ <article
95
+ className={`bg-white rounded-lg border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200 ${className}`}
96
+ aria-label={`Funnel: ${funnel.name}`}
97
+ >
98
+ {/* Header: Name + Status Badge */}
99
+ <header className="px-6 pt-5 pb-3 border-b border-gray-100">
100
+ <div className="flex items-start justify-between gap-3">
101
+ <h3 className="text-lg font-semibold text-gray-900 flex-1 min-w-0">
102
+ {funnel.name}
103
+ </h3>
104
+ <StatusBadge status={funnel.status} />
105
+ </div>
106
+ {funnel.description && (
107
+ <p className="mt-2 text-sm text-gray-600 line-clamp-2">
108
+ {funnel.description}
109
+ </p>
110
+ )}
111
+ </header>
112
+
113
+ {/* Stage List */}
114
+ <section
115
+ className="px-6 py-4 space-y-0"
116
+ aria-label="Funnel stages"
117
+ >
118
+ {funnel.stages.length === 0 ? (
119
+ <div className="text-sm text-gray-500 italic py-4 text-center">
120
+ No stages defined
121
+ </div>
122
+ ) : (
123
+ funnel.stages.map((stage, index) => (
124
+ <StageIndicator
125
+ key={stage.id}
126
+ order={index}
127
+ name={stage.name}
128
+ ruleCount={stage.rules.length}
129
+ isLast={index === funnel.stages.length - 1}
130
+ />
131
+ ))
132
+ )}
133
+ </section>
134
+
135
+ {/* Match Visualization */}
136
+ {hasRun && (
137
+ <section
138
+ className="px-6 py-4 border-t border-gray-100"
139
+ aria-label="Match results"
140
+ >
141
+ <MatchBar
142
+ matched={stats.matched}
143
+ total={stats.input}
144
+ />
145
+ </section>
146
+ )}
147
+
148
+ {/* Stats Row */}
149
+ {hasRun && (
150
+ <section
151
+ className="px-6 py-4 border-t border-gray-100"
152
+ aria-label="Funnel statistics"
153
+ >
154
+ <FunnelStats
155
+ input={stats.input}
156
+ matched={stats.matched}
157
+ excluded={stats.excluded}
158
+ />
159
+ </section>
160
+ )}
161
+
162
+ {/* No Run State */}
163
+ {!hasRun && (
164
+ <section
165
+ className="px-6 py-4 border-t border-gray-100 text-center"
166
+ aria-label="Funnel status"
167
+ >
168
+ <p className="text-sm text-gray-500">
169
+ {latestRun?.status === 'failed'
170
+ ? 'Last run failed'
171
+ : latestRun?.status === 'running'
172
+ ? 'Running...'
173
+ : 'No runs yet'}
174
+ </p>
175
+ </section>
176
+ )}
177
+
178
+ {/* Footer: Action Button */}
179
+ <footer className="px-6 py-4 border-t border-gray-100">
180
+ <button
181
+ onClick={handleViewFlow}
182
+ className="w-full inline-flex items-center justify-center gap-2 px-4 py-2.5 bg-gray-50 hover:bg-gray-100 text-gray-900 text-sm font-medium rounded-md transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
183
+ aria-label={`View flow details for ${funnel.name}`}
184
+ >
185
+ <span>View Flow</span>
186
+ <svg
187
+ className="w-4 h-4 transition-transform group-hover:translate-x-0.5"
188
+ fill="none"
189
+ viewBox="0 0 24 24"
190
+ stroke="currentColor"
191
+ aria-hidden="true"
192
+ >
193
+ <path
194
+ strokeLinecap="round"
195
+ strokeLinejoin="round"
196
+ strokeWidth={2}
197
+ d="M13 7l5 5m0 0l-5 5m5-5H6"
198
+ />
199
+ </svg>
200
+ </button>
201
+ </footer>
202
+ </article>
203
+ );
204
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * FunnelStats Component
3
+ *
4
+ * Three-column stats display showing INPUT / MATCHED / EXCLUDED counts.
5
+ *
6
+ * Design Rationale:
7
+ * - Three equal columns for balance
8
+ * - Color coding: Blue (input/neutral), Green (matched/success), Red (excluded/warning)
9
+ * - Large numbers draw attention to key metrics
10
+ * - Labels use uppercase for consistency with status badge
11
+ *
12
+ * Accessibility:
13
+ * - Semantic HTML (dl/dt/dd structure)
14
+ * - Color is supplementary (text labels provide meaning)
15
+ */
16
+
17
+ interface FunnelStatsProps {
18
+ input: number;
19
+ matched: number;
20
+ excluded: number;
21
+ className?: string;
22
+ }
23
+
24
+ export function FunnelStats({
25
+ input,
26
+ matched,
27
+ excluded,
28
+ className = ''
29
+ }: FunnelStatsProps) {
30
+ const stats = [
31
+ {
32
+ label: 'INPUT',
33
+ value: input,
34
+ color: 'text-blue-600',
35
+ bgColor: 'bg-blue-50'
36
+ },
37
+ {
38
+ label: 'MATCHED',
39
+ value: matched,
40
+ color: 'text-green-600',
41
+ bgColor: 'bg-green-50'
42
+ },
43
+ {
44
+ label: 'EXCLUDED',
45
+ value: excluded,
46
+ color: 'text-red-600',
47
+ bgColor: 'bg-red-50'
48
+ },
49
+ ];
50
+
51
+ return (
52
+ <dl className={`grid grid-cols-3 gap-2 ${className}`}>
53
+ {stats.map(({ label, value, color, bgColor }) => (
54
+ <div
55
+ key={label}
56
+ className={`${bgColor} rounded-lg px-3 py-2.5 text-center`}
57
+ >
58
+ <dt className="text-xs font-medium text-gray-600 mb-1">
59
+ {label}
60
+ </dt>
61
+ <dd className={`text-lg font-bold ${color}`}>
62
+ {value.toLocaleString()}
63
+ </dd>
64
+ </div>
65
+ ))}
66
+ </dl>
67
+ );
68
+ }