@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.
- package/package.json +9 -31
- package/src/api/README.md +507 -0
- package/src/api/adapter.ts +106 -0
- package/src/api/client.test.ts +640 -0
- package/src/api/client.ts +385 -0
- package/src/api/default-adapter.ts +243 -0
- package/src/api/index.ts +24 -0
- package/src/components/FilterRuleEditor/ARCHITECTURE.md +354 -0
- package/src/components/FilterRuleEditor/FieldSelector.tsx +91 -0
- package/src/components/FilterRuleEditor/FilterRuleEditor.stories.tsx +462 -0
- package/src/components/FilterRuleEditor/FilterRuleEditor.test.tsx +520 -0
- package/src/components/FilterRuleEditor/FilterRuleEditor.tsx +225 -0
- package/src/components/FilterRuleEditor/LogicToggle.tsx +64 -0
- package/src/components/FilterRuleEditor/OperatorSelector.tsx +75 -0
- package/src/components/FilterRuleEditor/README.md +291 -0
- package/src/components/FilterRuleEditor/RuleRow.tsx +246 -0
- package/src/components/FilterRuleEditor/ValueInputs/BooleanValueInput.tsx +54 -0
- package/src/components/FilterRuleEditor/ValueInputs/ChoiceValueInput.tsx +83 -0
- package/src/components/FilterRuleEditor/ValueInputs/DateValueInput.tsx +70 -0
- package/src/components/FilterRuleEditor/ValueInputs/MultiChoiceValueInput.tsx +132 -0
- package/src/components/FilterRuleEditor/ValueInputs/NumberValueInput.tsx +73 -0
- package/src/components/FilterRuleEditor/ValueInputs/TextValueInput.tsx +50 -0
- package/src/components/FilterRuleEditor/ValueInputs/index.ts +12 -0
- package/src/components/FilterRuleEditor/constants.ts +64 -0
- package/src/components/FilterRuleEditor/index.ts +14 -0
- package/src/components/FunnelCard/DESIGN.md +447 -0
- package/src/components/FunnelCard/FunnelCard.stories.tsx +484 -0
- package/src/components/FunnelCard/FunnelCard.test.ts +257 -0
- package/src/components/FunnelCard/FunnelCard.test.tsx +336 -0
- package/src/components/FunnelCard/FunnelCard.tsx +204 -0
- package/src/components/FunnelCard/FunnelStats.tsx +68 -0
- package/src/components/FunnelCard/IMPLEMENTATION_SUMMARY.md +505 -0
- package/src/components/FunnelCard/INSTALLATION.md +304 -0
- package/src/components/FunnelCard/MatchBar.tsx +49 -0
- package/src/components/FunnelCard/README.md +294 -0
- package/src/components/FunnelCard/StageIndicator.tsx +62 -0
- package/src/components/FunnelCard/StatusBadge.tsx +52 -0
- package/src/components/FunnelCard/index.ts +14 -0
- package/src/components/FunnelPreview/EntityCard.tsx +72 -0
- package/src/components/FunnelPreview/FunnelPreview.stories.tsx +227 -0
- package/src/components/FunnelPreview/FunnelPreview.test.tsx +316 -0
- package/src/components/FunnelPreview/FunnelPreview.tsx +249 -0
- package/src/components/FunnelPreview/LoadingPreview.tsx +60 -0
- package/src/components/FunnelPreview/PreviewStats.tsx +78 -0
- package/src/components/FunnelPreview/README.md +337 -0
- package/src/components/FunnelPreview/StageBreakdown.tsx +94 -0
- package/src/components/FunnelPreview/example.tsx +286 -0
- package/src/components/FunnelPreview/index.ts +14 -0
- package/src/components/FunnelRunHistory/COMPONENT_SUMMARY.md +246 -0
- package/src/components/FunnelRunHistory/FunnelRunHistory.stories.tsx +272 -0
- package/src/components/FunnelRunHistory/FunnelRunHistory.test.tsx +323 -0
- package/src/components/FunnelRunHistory/FunnelRunHistory.tsx +329 -0
- package/src/components/FunnelRunHistory/README.md +325 -0
- package/src/components/FunnelRunHistory/RunActions.tsx +168 -0
- package/src/components/FunnelRunHistory/RunDetailsModal.tsx +221 -0
- package/src/components/FunnelRunHistory/RunFilters.tsx +128 -0
- package/src/components/FunnelRunHistory/RunRow.tsx +122 -0
- package/src/components/FunnelRunHistory/RunStatusBadge.tsx +75 -0
- package/src/components/FunnelRunHistory/StageBreakdownList.tsx +110 -0
- package/src/components/FunnelRunHistory/index.ts +51 -0
- package/src/components/FunnelRunHistory/types.ts +40 -0
- package/src/components/FunnelRunHistory/utils.test.ts +126 -0
- package/src/components/FunnelRunHistory/utils.ts +100 -0
- package/src/components/FunnelStageBuilder/AddStageButton.tsx +52 -0
- package/src/components/FunnelStageBuilder/FunnelStageBuilder.css +413 -0
- package/src/components/FunnelStageBuilder/FunnelStageBuilder.stories.tsx +312 -0
- package/src/components/FunnelStageBuilder/FunnelStageBuilder.test.tsx +304 -0
- package/src/components/FunnelStageBuilder/FunnelStageBuilder.tsx +321 -0
- package/src/components/FunnelStageBuilder/README.md +341 -0
- package/src/components/FunnelStageBuilder/StageActions.test.tsx +205 -0
- package/src/components/FunnelStageBuilder/StageActions.tsx +126 -0
- package/src/components/FunnelStageBuilder/StageCard.tsx +202 -0
- package/src/components/FunnelStageBuilder/StageForm.tsx +262 -0
- package/src/components/FunnelStageBuilder/TagInput.test.tsx +178 -0
- package/src/components/FunnelStageBuilder/TagInput.tsx +129 -0
- package/src/components/FunnelStageBuilder/index.ts +21 -0
- package/src/components/FunnelVisualFlow/FlowLegend.tsx +77 -0
- package/{dist/components/index.css → src/components/FunnelVisualFlow/FunnelVisualFlow.css} +89 -13
- package/src/components/FunnelVisualFlow/FunnelVisualFlow.stories.tsx +254 -0
- package/src/components/FunnelVisualFlow/FunnelVisualFlow.test.tsx +208 -0
- package/src/components/FunnelVisualFlow/FunnelVisualFlow.tsx +229 -0
- package/src/components/FunnelVisualFlow/README.md +323 -0
- package/src/components/FunnelVisualFlow/StageNode.tsx +188 -0
- package/src/components/FunnelVisualFlow/example.tsx +227 -0
- package/src/components/FunnelVisualFlow/index.ts +10 -0
- package/src/components/index.ts +102 -0
- package/src/core/README.md +307 -0
- package/src/core/engine.test.ts +1087 -0
- package/src/core/engine.ts +329 -0
- package/src/core/evaluator.example.ts +353 -0
- package/src/core/evaluator.test.ts +639 -0
- package/src/core/evaluator.ts +261 -0
- package/src/core/field-resolver.example.ts +175 -0
- package/src/core/field-resolver.test.ts +541 -0
- package/src/core/field-resolver.ts +247 -0
- package/src/core/index.ts +34 -0
- package/src/core/operators.test.ts +539 -0
- package/src/core/operators.ts +241 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useDebouncedValue.ts +28 -0
- package/src/index.ts +155 -0
- package/src/store/README.md +342 -0
- package/src/store/create-funnel-store.test.ts +686 -0
- package/src/store/create-funnel-store.ts +538 -0
- package/src/store/index.ts +9 -0
- package/src/store/types.ts +294 -0
- package/src/stories/CrossDomain.stories.tsx +149 -0
- package/src/stories/Welcome.stories.tsx +81 -0
- package/src/stories/demo-data/index.ts +3 -0
- package/src/stories/demo-data/investors.ts +216 -0
- package/src/stories/demo-data/leads.ts +223 -0
- package/src/stories/demo-data/recipes.ts +217 -0
- package/src/test/setup.ts +5 -0
- package/src/types/index.ts +843 -0
- package/dist/client-3ESO2NHy.d.ts +0 -310
- package/dist/client-CZu03ACp.d.cts +0 -310
- package/dist/components/index.cjs +0 -3243
- package/dist/components/index.cjs.map +0 -1
- package/dist/components/index.css.map +0 -1
- package/dist/components/index.d.cts +0 -726
- package/dist/components/index.d.ts +0 -726
- package/dist/components/index.js +0 -3196
- package/dist/components/index.js.map +0 -1
- package/dist/core/index.cjs +0 -500
- package/dist/core/index.cjs.map +0 -1
- package/dist/core/index.d.cts +0 -359
- package/dist/core/index.d.ts +0 -359
- package/dist/core/index.js +0 -486
- package/dist/core/index.js.map +0 -1
- package/dist/hooks/index.cjs +0 -21
- package/dist/hooks/index.cjs.map +0 -1
- package/dist/hooks/index.d.cts +0 -11
- package/dist/hooks/index.d.ts +0 -11
- package/dist/hooks/index.js +0 -19
- package/dist/hooks/index.js.map +0 -1
- package/dist/index-BGDEXbuz.d.cts +0 -434
- package/dist/index-BGDEXbuz.d.ts +0 -434
- package/dist/index.cjs +0 -4499
- package/dist/index.cjs.map +0 -1
- package/dist/index.css +0 -198
- package/dist/index.css.map +0 -1
- package/dist/index.d.cts +0 -99
- package/dist/index.d.ts +0 -99
- package/dist/index.js +0 -4421
- package/dist/index.js.map +0 -1
- package/dist/store/index.cjs +0 -391
- package/dist/store/index.cjs.map +0 -1
- package/dist/store/index.d.cts +0 -225
- package/dist/store/index.d.ts +0 -225
- package/dist/store/index.js +0 -388
- 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
|
+
}
|