@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.
- 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 -3241
- 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 -3194
- 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 -20
- 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 -18
- 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 -389
- 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 -386
- 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
|