@umituz/web-dashboard 6.0.0 → 6.2.0

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 (133) hide show
  1. package/README.md +47 -73
  2. package/package.json +15 -7
  3. package/src/config/DashboardConfig.ts +19 -68
  4. package/src/domains/analytics/components/index.ts +0 -6
  5. package/src/domains/analytics/hooks/index.ts +0 -6
  6. package/src/domains/analytics/hooks/useAnalytics.ts +2 -20
  7. package/src/domains/analytics/index.ts +0 -10
  8. package/src/domains/analytics/services/AnalyticsEngineService.ts +7 -74
  9. package/src/domains/analytics/services/PerformanceService.ts +27 -138
  10. package/src/domains/analytics/services/index.ts +0 -4
  11. package/src/domains/analytics/types/analytics.ts +100 -270
  12. package/src/domains/analytics/types/index.ts +0 -6
  13. package/src/domains/analytics/utils/analytics.test.ts +2 -9
  14. package/src/domains/analytics/utils/analytics.ts +1 -22
  15. package/src/domains/analytics/utils/index.ts +0 -6
  16. package/src/domains/calendar/config.ts +0 -7
  17. package/src/domains/calendar/hooks/index.ts +0 -4
  18. package/src/domains/calendar/hooks/useCalendar.ts +7 -27
  19. package/src/domains/calendar/index.ts +0 -4
  20. package/src/domains/calendar/services/CalendarService.ts +0 -21
  21. package/src/domains/calendar/services/index.ts +0 -4
  22. package/src/domains/calendar/types/calendar.types.ts +9 -53
  23. package/src/domains/calendar/utils/contentItemMapper.test.ts +0 -4
  24. package/src/domains/calendar/utils/contentItemMapper.ts +0 -31
  25. package/src/domains/calendar/utils/index.ts +0 -26
  26. package/src/domains/layouts/components/index.ts +0 -4
  27. package/src/domains/layouts/hooks/dashboard.ts +0 -21
  28. package/src/domains/layouts/hooks/index.ts +0 -4
  29. package/src/domains/layouts/index.ts +0 -6
  30. package/src/domains/layouts/theme/default.ts +0 -12
  31. package/src/domains/layouts/theme/index.ts +0 -4
  32. package/src/domains/layouts/theme/presets.ts +0 -9
  33. package/src/domains/layouts/theme/utils.ts +0 -15
  34. package/src/domains/layouts/types/index.ts +0 -4
  35. package/src/domains/layouts/types/layout.ts +18 -56
  36. package/src/domains/layouts/types/notification.ts +4 -17
  37. package/src/domains/layouts/types/sidebar.ts +8 -28
  38. package/src/domains/layouts/types/theme.ts +23 -59
  39. package/src/domains/layouts/types/user.ts +9 -30
  40. package/src/domains/layouts/utils/dashboard.ts +4 -48
  41. package/src/domains/layouts/utils/index.ts +0 -4
  42. package/src/domains/modules/build.tsx +2 -16
  43. package/src/domains/modules/types.ts +8 -23
  44. package/src/domains/onboarding/calc/calc.test.ts +173 -0
  45. package/src/domains/onboarding/calc/card-stack-motion-curve.ts +27 -0
  46. package/src/domains/onboarding/calc/count-with-locale.ts +8 -0
  47. package/src/domains/onboarding/calc/duration-equal-split.ts +12 -0
  48. package/src/domains/onboarding/calc/elapsed-millis.ts +6 -0
  49. package/src/domains/onboarding/calc/index.ts +36 -0
  50. package/src/domains/onboarding/calc/progress-percentage.ts +27 -0
  51. package/src/domains/onboarding/calc/slider-value-bounds.ts +34 -0
  52. package/src/domains/onboarding/calc/step-index-clamp.ts +19 -0
  53. package/src/domains/onboarding/calc/swipe-gesture-curve.ts +54 -0
  54. package/src/domains/onboarding/components/AnimationWrapper.tsx +69 -0
  55. package/src/domains/onboarding/components/OnboardingErrorBoundary.tsx +61 -0
  56. package/src/domains/onboarding/components/OnboardingFlow.test.tsx +459 -0
  57. package/src/domains/onboarding/components/OnboardingFlow.tsx +313 -0
  58. package/src/domains/onboarding/components/OnboardingIntl.tsx +98 -0
  59. package/src/domains/onboarding/components/PrimaryButton.tsx +40 -0
  60. package/src/domains/onboarding/components/ProgressBar.tsx +32 -0
  61. package/src/domains/onboarding/components/QuestionWrapper.tsx +28 -0
  62. package/src/domains/onboarding/components/ScreenContainer.tsx +19 -0
  63. package/src/domains/onboarding/components/SwipeCard.test.tsx +49 -0
  64. package/src/domains/onboarding/components/SwipeCard.tsx +88 -0
  65. package/src/domains/onboarding/components/renderBundledIcon.tsx +7 -0
  66. package/src/domains/onboarding/components/screens/AccountGateScreen.tsx +79 -0
  67. package/src/domains/onboarding/components/screens/AppDemoScreen.tsx +33 -0
  68. package/src/domains/onboarding/components/screens/ComparisonScreen.tsx +49 -0
  69. package/src/domains/onboarding/components/screens/GoalQuestionScreen.tsx +59 -0
  70. package/src/domains/onboarding/components/screens/PainPointsScreen.tsx +65 -0
  71. package/src/domains/onboarding/components/screens/PaywallScreen.tsx +122 -0
  72. package/src/domains/onboarding/components/screens/PermissionPrimingScreen.tsx +98 -0
  73. package/src/domains/onboarding/components/screens/PersonalizedSolutionScreen.tsx +40 -0
  74. package/src/domains/onboarding/components/screens/PreferencesScreen.tsx +180 -0
  75. package/src/domains/onboarding/components/screens/ProcessingScreen.tsx +63 -0
  76. package/src/domains/onboarding/components/screens/SocialProofScreen.tsx +87 -0
  77. package/src/domains/onboarding/components/screens/TinderCardsScreen.tsx +91 -0
  78. package/src/domains/onboarding/components/screens/ValueDeliveryScreen.tsx +69 -0
  79. package/src/domains/onboarding/components/screens/WelcomeScreen.tsx +32 -0
  80. package/src/domains/onboarding/components/screens/index.ts +28 -0
  81. package/src/domains/onboarding/hooks/index.ts +7 -0
  82. package/src/domains/onboarding/hooks/useOnboardingAnalytics.test.tsx +74 -0
  83. package/src/domains/onboarding/hooks/useOnboardingAnalytics.ts +49 -0
  84. package/src/domains/onboarding/hooks/useOnboardingProgress.test.tsx +150 -0
  85. package/src/domains/onboarding/hooks/useOnboardingProgress.ts +249 -0
  86. package/src/domains/onboarding/hooks/usePermissionPriming.ts +45 -0
  87. package/src/domains/onboarding/hooks/useReducedMotion.ts +19 -0
  88. package/src/domains/onboarding/index.ts +153 -0
  89. package/src/domains/onboarding/predicates/has-handler.ts +21 -0
  90. package/src/domains/onboarding/predicates/icon-name-resolves.ts +97 -0
  91. package/src/domains/onboarding/predicates/index.ts +26 -0
  92. package/src/domains/onboarding/predicates/permission-granted.ts +21 -0
  93. package/src/domains/onboarding/predicates/permission-supports.ts +56 -0
  94. package/src/domains/onboarding/predicates/predicates.test.ts +188 -0
  95. package/src/domains/onboarding/predicates/screen-emits-data.ts +13 -0
  96. package/src/domains/onboarding/predicates/storage-available.ts +14 -0
  97. package/src/domains/onboarding/templates/TemplateDemoFallback.tsx +92 -0
  98. package/src/domains/onboarding/templates/art-onboarding.ts +71 -0
  99. package/src/domains/onboarding/templates/health-onboarding.ts +92 -0
  100. package/src/domains/onboarding/templates/index.ts +6 -0
  101. package/src/domains/onboarding/templates/platform-onboarding.ts +201 -0
  102. package/src/domains/onboarding/templates/video-onboarding.ts +95 -0
  103. package/src/domains/onboarding/test/setup.ts +23 -0
  104. package/src/domains/onboarding/types/config.ts +57 -0
  105. package/src/domains/onboarding/types/index.ts +10 -0
  106. package/src/domains/onboarding/types/labels.ts +49 -0
  107. package/src/domains/onboarding/types/screens.ts +185 -0
  108. package/src/domains/onboarding/utils/analytics-dispatch.test.ts +93 -0
  109. package/src/domains/onboarding/utils/analytics-dispatch.ts +73 -0
  110. package/src/domains/onboarding/utils/default-storage-key.ts +1 -0
  111. package/src/domains/onboarding/utils/index.ts +12 -0
  112. package/src/domains/onboarding/utils/local-storage-bridge.test.ts +93 -0
  113. package/src/domains/onboarding/utils/local-storage-bridge.ts +84 -0
  114. package/src/domains/onboarding/utils/permission-bridge.test.ts +64 -0
  115. package/src/domains/onboarding/utils/permission-bridge.ts +87 -0
  116. package/src/domains/onboarding/validation/flow-structure-check.ts +22 -0
  117. package/src/domains/onboarding/validation/index.ts +9 -0
  118. package/src/domains/onboarding/validation/permission-status-check.ts +18 -0
  119. package/src/domains/onboarding/validation/screen-config-check.ts +100 -0
  120. package/src/domains/onboarding/validation/stored-progress-check.ts +59 -0
  121. package/src/domains/onboarding/validation/validation.test.ts +170 -0
  122. package/src/domains/permissions/hasPermission.ts +0 -8
  123. package/src/domains/permissions/types.ts +0 -20
  124. package/src/domains/settings/components/index.ts +0 -4
  125. package/src/domains/settings/hooks/index.ts +0 -4
  126. package/src/domains/settings/hooks/useSettings.ts +1 -26
  127. package/src/domains/settings/index.ts +0 -6
  128. package/src/domains/settings/types/index.ts +0 -4
  129. package/src/domains/settings/types/settings.ts +23 -67
  130. package/src/domains/settings/utils/index.ts +0 -4
  131. package/src/domains/settings/utils/settings.ts +0 -45
  132. package/src/styles/dashboard.contract.test.ts +1 -0
  133. package/src/styles/dashboard.css +818 -1
@@ -0,0 +1,201 @@
1
+ import type { OnboardingConfig } from '../types/config';
2
+ import type { OnboardingScreen } from '../types/screens';
3
+ import { withDefaultDemo, defaultDemoResult } from './TemplateDemoFallback';
4
+
5
+ const screens: OnboardingScreen[] = [
6
+ {
7
+ id: 'welcome',
8
+ type: 'welcome',
9
+ title: 'Welcome aboard',
10
+ subtitle: 'A few questions and we’ll tailor the entire experience.',
11
+ cta: 'Start',
12
+ heroIconName: 'Rocket',
13
+ highlight: '~2 minutes',
14
+ },
15
+ {
16
+ id: 'goals',
17
+ type: 'goal-question',
18
+ question: 'What do you want to get out of this?',
19
+ multiSelect: true,
20
+ options: [
21
+ { id: 'productivity', label: 'Be more productive', iconName: 'Zap' },
22
+ { id: 'learn', label: 'Learn new skills', iconName: 'GraduationCap' },
23
+ { id: 'connect', label: 'Connect with people', iconName: 'Users' },
24
+ { id: 'create', label: 'Create content', iconName: 'PenTool' },
25
+ ],
26
+ onChange: () => undefined,
27
+ cta: 'Continue',
28
+ },
29
+ {
30
+ id: 'pains',
31
+ type: 'pain-points',
32
+ title: 'What frustrates you about tools like this?',
33
+ painPoints: [
34
+ { id: 'complex', label: 'Too complex' },
35
+ { id: 'slow', label: 'Too slow' },
36
+ { id: 'pricey', label: 'Too expensive' },
37
+ { id: 'privacy', label: 'Privacy concerns' },
38
+ ],
39
+ multiSelect: true,
40
+ maxSelect: 3,
41
+ onChange: () => undefined,
42
+ cta: 'Continue',
43
+ },
44
+ {
45
+ id: 'proof',
46
+ type: 'social-proof',
47
+ title: 'Why people stay',
48
+ testimonials: [
49
+ {
50
+ name: 'Maya K.',
51
+ role: 'Designer',
52
+ content: 'The best $10/mo I spend. Period.',
53
+ rating: 5,
54
+ },
55
+ {
56
+ name: 'Andre P.',
57
+ role: 'PM',
58
+ content: 'I onboarded my whole team in a day.',
59
+ rating: 5,
60
+ },
61
+ {
62
+ name: 'Sara L.',
63
+ role: 'Founder',
64
+ content: 'Replaced three other tools for me.',
65
+ rating: 4,
66
+ },
67
+ ],
68
+ },
69
+ {
70
+ id: 'tinder',
71
+ type: 'tinder-cards',
72
+ title: 'A bit about you',
73
+ statements: [
74
+ { id: 'a', text: 'I prefer learning by doing.', category: 'Style' },
75
+ { id: 'b', text: 'I read every doc end-to-end.', category: 'Style' },
76
+ { id: 'c', text: 'I collaborate with others daily.', category: 'Style' },
77
+ { id: 'd', text: 'I work alone most of the time.', category: 'Style' },
78
+ ],
79
+ onChange: () => undefined,
80
+ cta: 'Continue',
81
+ },
82
+ {
83
+ id: 'personalized',
84
+ type: 'personalized-solution',
85
+ title: 'Built for the way you work',
86
+ subtitle: 'Here is what we’ll set up for you.',
87
+ solutions: [
88
+ { painPointId: 'complex', solution: 'A guided first-run experience with smart defaults.' },
89
+ { painPointId: 'slow', solution: 'Sub-200ms interactions, no spinner purgatory.' },
90
+ { painPointId: 'pricey', solution: 'A free tier that actually does the job.' },
91
+ { painPointId: 'privacy', solution: 'You own your data — export and delete anytime.' },
92
+ ],
93
+ selectedPainPointIds: [],
94
+ },
95
+ {
96
+ id: 'comparison',
97
+ type: 'comparison',
98
+ title: 'Before vs. after',
99
+ before: ['Tool overload', 'Generic templates', 'Steep learning curve'],
100
+ after: ['One workspace', 'Personalized setup', '2-minute onboarding'],
101
+ },
102
+ {
103
+ id: 'prefs',
104
+ type: 'preferences',
105
+ title: 'A few preferences',
106
+ preferences: [
107
+ {
108
+ id: 'theme',
109
+ label: 'Theme',
110
+ type: 'select',
111
+ defaultValue: 'system',
112
+ options: [
113
+ { id: 'system', label: 'Match system' },
114
+ { id: 'light', label: 'Light' },
115
+ { id: 'dark', label: 'Dark' },
116
+ ],
117
+ },
118
+ {
119
+ id: 'density',
120
+ label: 'Density',
121
+ type: 'select',
122
+ defaultValue: 'comfortable',
123
+ options: [
124
+ { id: 'compact', label: 'Compact' },
125
+ { id: 'comfortable', label: 'Comfortable' },
126
+ ],
127
+ },
128
+ ],
129
+ onChange: () => undefined,
130
+ cta: 'Continue',
131
+ },
132
+ {
133
+ id: 'permission-notifications',
134
+ type: 'permission-priming',
135
+ permission: 'notifications',
136
+ title: 'Stay in the loop',
137
+ description: 'Only the alerts you actually want.',
138
+ benefits: ['Mentions & replies', 'Weekly summary', 'Customizable quiet hours'],
139
+ iconName: 'Bell',
140
+ onRequest: async () => 'prompt',
141
+ cta: 'Enable notifications',
142
+ },
143
+ {
144
+ id: 'processing',
145
+ type: 'processing',
146
+ title: 'Setting up your workspace…',
147
+ steps: ['Tuning recommendations', 'Importing defaults', 'Warming caches'],
148
+ durationMs: 3500,
149
+ onComplete: () => undefined,
150
+ },
151
+ {
152
+ id: 'demo',
153
+ type: 'app-demo',
154
+ title: 'Quick tour — try a quick action',
155
+ demo: { component: () => null, onSave: () => undefined },
156
+ },
157
+ {
158
+ id: 'value',
159
+ type: 'value-delivery',
160
+ title: 'You’re all set',
161
+ result: defaultDemoResult(),
162
+ },
163
+ {
164
+ id: 'account',
165
+ type: 'account-gate',
166
+ title: 'Save your progress',
167
+ subtitle: 'Sign in to sync across devices.',
168
+ optional: true,
169
+ signInMethod: 'email',
170
+ skipText: 'Continue without account',
171
+ onSignIn: async () => ({ signedIn: true, method: 'email' }),
172
+ },
173
+ {
174
+ id: 'paywall',
175
+ type: 'paywall',
176
+ title: 'Unlock the full platform',
177
+ subtitle: 'Pricing that scales with you.',
178
+ price: '$14.99 / month',
179
+ trialDays: 14,
180
+ cta: 'Start 14-day trial',
181
+ secondaryCta: 'Stay on free',
182
+ features: [
183
+ { iconName: 'Infinity', text: 'Unlimited projects' },
184
+ { iconName: 'Users', text: 'Team workspaces' },
185
+ { iconName: 'Shield', text: 'Enterprise SSO' },
186
+ { iconName: 'Headphones', text: 'Priority support' },
187
+ ],
188
+ socialProof: { text: 'Joined by', count: 850000 },
189
+ onSecondaryCta: () => undefined,
190
+ onAccept: async () => ({ accepted: true, trialStarted: true }),
191
+ },
192
+ ];
193
+
194
+ const resolvedScreens = screens.map((s) => withDefaultDemo(s));
195
+
196
+ export const platformOnboardingTemplate: OnboardingConfig = {
197
+ category: 'platform',
198
+ screens: resolvedScreens,
199
+ onSkip: () => undefined,
200
+ onComplete: () => undefined,
201
+ };
@@ -0,0 +1,95 @@
1
+ import type { OnboardingConfig } from '../types/config';
2
+ import type { OnboardingScreen } from '../types/screens';
3
+
4
+ const screens: OnboardingScreen[] = [
5
+ {
6
+ id: 'welcome',
7
+ type: 'welcome',
8
+ title: 'Watch what you actually love',
9
+ subtitle: 'A few quick questions and we’ll never recommend junk again.',
10
+ cta: 'Let’s go',
11
+ heroIconName: 'Play',
12
+ },
13
+ {
14
+ id: 'genres',
15
+ type: 'goal-question',
16
+ question: 'Which genres do you enjoy?',
17
+ multiSelect: true,
18
+ options: [
19
+ { id: 'comedy', label: 'Comedy', iconName: 'Smile' },
20
+ { id: 'drama', label: 'Drama', iconName: 'Theater' },
21
+ { id: 'documentary', label: 'Documentary', iconName: 'BookOpen' },
22
+ { id: 'thriller', label: 'Thriller', iconName: 'AlertTriangle' },
23
+ { id: 'sci-fi', label: 'Sci-fi', iconName: 'Rocket' },
24
+ ],
25
+ onChange: () => undefined,
26
+ cta: 'Continue',
27
+ },
28
+ {
29
+ id: 'mood',
30
+ type: 'tinder-cards',
31
+ title: 'Swipe on what feels right',
32
+ statements: [
33
+ { id: 's1', text: 'I want to learn something new today.', category: 'Curious' },
34
+ { id: 's2', text: 'I want pure entertainment.', category: 'Fun' },
35
+ { id: 's3', text: 'I want something to cry to.', category: 'Emotional' },
36
+ { id: 's4', text: 'I want background noise while I work.', category: 'Casual' },
37
+ ],
38
+ onChange: () => undefined,
39
+ cta: 'Continue',
40
+ },
41
+ {
42
+ id: 'length',
43
+ type: 'preferences',
44
+ title: 'How long should a video be?',
45
+ preferences: [
46
+ {
47
+ id: 'length',
48
+ label: 'Preferred length',
49
+ type: 'slider',
50
+ min: 1,
51
+ max: 120,
52
+ defaultValue: 20,
53
+ },
54
+ ],
55
+ onChange: () => undefined,
56
+ cta: 'Continue',
57
+ },
58
+ {
59
+ id: 'permission-priming-notifications',
60
+ type: 'permission-priming',
61
+ permission: 'notifications',
62
+ title: 'Never miss a live premiere',
63
+ description: 'Get a heads-up when channels you love go live.',
64
+ benefits: ['Only from channels you follow', 'Snooze any time', 'Silent by default'],
65
+ iconName: 'Bell',
66
+ onRequest: async () => 'prompt',
67
+ cta: 'Turn on alerts',
68
+ },
69
+ {
70
+ id: 'paywall',
71
+ type: 'paywall',
72
+ title: 'Watch without limits',
73
+ subtitle: 'Ad-free, offline downloads, 4K.',
74
+ price: '$11.99 / month',
75
+ trialDays: 14,
76
+ cta: 'Start 14-day trial',
77
+ secondaryCta: 'Continue with ads',
78
+ features: [
79
+ { iconName: 'Tv', text: '4K Ultra HD' },
80
+ { iconName: 'Download', text: 'Offline downloads' },
81
+ { iconName: 'Users', text: '4 screens at once' },
82
+ { iconName: 'Ban', text: 'Zero ads' },
83
+ ],
84
+ socialProof: { text: 'Loved by', count: 1200000 },
85
+ onSecondaryCta: () => undefined,
86
+ onAccept: async () => ({ accepted: true, trialStarted: true }),
87
+ },
88
+ ];
89
+
90
+ export const videoOnboardingTemplate: OnboardingConfig = {
91
+ category: 'video',
92
+ screens,
93
+ onSkip: () => undefined,
94
+ onComplete: () => undefined,
95
+ };
@@ -0,0 +1,23 @@
1
+ import { afterEach, vi } from 'vitest';
2
+ import { cleanup } from '@testing-library/react';
3
+
4
+ (globalThis as Record<string, unknown>).IS_REACT_ACT_ENVIRONMENT = true;
5
+
6
+ if (typeof window !== 'undefined')
7
+ Object.defineProperty(window, 'matchMedia', {
8
+ writable: true,
9
+ value: (query: string): MediaQueryList =>
10
+ ({
11
+ matches: query.includes('prefers-reduced-motion'),
12
+ media: query,
13
+ onchange: null,
14
+ addListener: vi.fn(), removeListener: vi.fn(),
15
+ addEventListener: vi.fn(),
16
+ removeEventListener: vi.fn(),
17
+ dispatchEvent: vi.fn(),
18
+ }) as unknown as MediaQueryList,
19
+ });
20
+
21
+ afterEach(() => {
22
+ cleanup();
23
+ });
@@ -0,0 +1,57 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { DemoConfig, OnboardingScreen, PaywallScreenConfig } from './screens';
3
+ import type { OnboardingLabels } from './labels';
4
+
5
+ export type { OnboardingLabels } from './labels';
6
+
7
+ export type OnboardingCategory =
8
+ | 'health'
9
+ | 'art'
10
+ | 'video'
11
+ | 'security'
12
+ | 'education'
13
+ | 'platform'
14
+ | 'lifestyle';
15
+
16
+ export type AnalyticsProvider = 'google-analytics' | 'amplitude' | 'mixpanel' | 'custom';
17
+
18
+ export interface AnalyticsConfig {
19
+ trackEvents: boolean;
20
+ provider?: AnalyticsProvider;
21
+ customTracker?: (event: OnboardingAnalyticsEvent) => void;
22
+ }
23
+
24
+ export type OnboardingAnalyticsEvent =
25
+ | { name: 'onboarding_started'; category: OnboardingCategory; timestamp: number }
26
+ | { name: 'onboarding_step_completed'; stepId: string; stepType: string; index: number; timestamp: number }
27
+ | { name: 'onboarding_step_skipped'; stepId: string; index: number; timestamp: number }
28
+ | { name: 'onboarding_completed'; durationMs: number; timestamp: number }
29
+ | { name: 'onboarding_abandoned'; lastStepId: string; durationMs: number; timestamp: number }
30
+ | { name: 'paywall_viewed'; stepId: string; timestamp: number }
31
+ | { name: 'paywall_accepted'; stepId: string; price: string; timestamp: number }
32
+ | { name: 'paywall_declined'; stepId: string; timestamp: number }
33
+ | { name: 'permission_requested'; permission: string; stepId: string; timestamp: number }
34
+ | { name: 'permission_granted'; permission: string; stepId: string; timestamp: number }
35
+ | { name: 'permission_denied'; permission: string; stepId: string; timestamp: number };
36
+
37
+ export interface OnboardingConfig {
38
+ category: OnboardingCategory;
39
+ screens: ReadonlyArray<OnboardingScreen>;
40
+ demo?: DemoConfig;
41
+ paywall?: PaywallScreenConfig;
42
+ analytics?: AnalyticsConfig;
43
+ labels?: Partial<OnboardingLabels>;
44
+ locale?: string;
45
+ headerSlot?: ReactNode;
46
+ footerSlot?: ReactNode;
47
+ onComplete?: (data: OnboardingResult) => void;
48
+ onSkip?: () => void;
49
+ storageKey?: string;
50
+ }
51
+
52
+ export interface OnboardingResult {
53
+ completedSteps: ReadonlyArray<string>;
54
+ collected: Record<string, unknown>;
55
+ durationMs: number;
56
+ finishedAt: Date;
57
+ }
@@ -0,0 +1,10 @@
1
+ export type { OnboardingScreen, OnboardingScreenType, OnboardingOption } from './screens';
2
+ export type {
3
+ OnboardingConfig,
4
+ OnboardingCategory,
5
+ AnalyticsConfig,
6
+ AnalyticsProvider,
7
+ OnboardingAnalyticsEvent,
8
+ OnboardingResult,
9
+ OnboardingLabels,
10
+ } from './config';
@@ -0,0 +1,49 @@
1
+ export interface OnboardingLabels {
2
+ continueAction: string;
3
+ back: string;
4
+ skip: string;
5
+ loading: string;
6
+ stepOfPattern: string;
7
+ countOfPattern: string;
8
+
9
+ selectAllThatApply: string;
10
+ selectOne: string;
11
+ pickUpToPattern: string;
12
+ pickTheOne: string;
13
+ tailorExperience: string;
14
+
15
+ agree: string;
16
+ disagree: string;
17
+ swipeHint: string;
18
+ allDone: string;
19
+
20
+ withoutUs: string;
21
+ withUs: string;
22
+
23
+ previous: string;
24
+ nextAction: string;
25
+
26
+ continueWithResult: string;
27
+
28
+ allowAccess: string;
29
+ permissionRequestFailed: string;
30
+
31
+ continueWithEmail: string;
32
+ continueWithGoogle: string;
33
+ continueWithApple: string;
34
+ continueWithGitHub: string;
35
+ skipForNow: string;
36
+ signInFailed: string;
37
+
38
+ couldNotStartTrial: string;
39
+ trialDaysPattern: string;
40
+ trialIntroPattern: string;
41
+ cancelAnytimeNote: string;
42
+
43
+ share: string;
44
+ copied: string;
45
+
46
+ somethingWentWrong: string;
47
+ unexpectedError: string;
48
+ tryAgain: string;
49
+ }
@@ -0,0 +1,185 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ export interface BaseScreen<T extends string, D = unknown> {
4
+ id: string;
5
+ type: T;
6
+ title?: string;
7
+ subtitle?: string;
8
+ cta?: string;
9
+ onCta?: () => void;
10
+ data?: D;
11
+ }
12
+
13
+ export interface OnboardingOption {
14
+ id: string;
15
+ label: string;
16
+ description?: string;
17
+ iconName?: string; image?: string;
18
+ }
19
+
20
+ export type WelcomeScreenConfig = BaseScreen<'welcome'> & {
21
+ heroIconName?: string;
22
+ highlight?: string;
23
+ };
24
+
25
+ export type GoalQuestionScreenConfig = BaseScreen<'goal-question', string[]> & {
26
+ question: string;
27
+ options: OnboardingOption[];
28
+ multiSelect?: boolean;
29
+ onChange: (selectedIds: string[]) => void;
30
+ };
31
+
32
+ export type PainPointsScreenConfig = BaseScreen<'pain-points', string[]> & {
33
+ title: string;
34
+ painPoints: OnboardingOption[];
35
+ multiSelect?: boolean;
36
+ maxSelect?: number;
37
+ onChange: (selectedIds: string[]) => void;
38
+ };
39
+
40
+ export type SocialProofScreenConfig = BaseScreen<'social-proof'> & {
41
+ testimonials: ReadonlyArray<{
42
+ name: string;
43
+ role: string;
44
+ content: string;
45
+ avatar?: string;
46
+ rating?: number;
47
+ }>;
48
+ };
49
+
50
+ export type TinderCardsScreenConfig = BaseScreen<'tinder-cards', string[]> & {
51
+ statements: ReadonlyArray<{
52
+ id: string;
53
+ text: string;
54
+ category: string;
55
+ }>;
56
+ onChange: (likedIds: string[]) => void;
57
+ };
58
+
59
+ export type PersonalizedSolutionScreenConfig = BaseScreen<'personalized-solution'> & {
60
+ solutions: ReadonlyArray<{
61
+ painPointId: string;
62
+ solution: string;
63
+ statistic?: string;
64
+ }>;
65
+ selectedPainPointIds: ReadonlyArray<string>;
66
+ };
67
+
68
+ export type ComparisonScreenConfig = BaseScreen<'comparison'> & {
69
+ before: ReadonlyArray<string>;
70
+ after: ReadonlyArray<string>;
71
+ };
72
+
73
+ export type PreferenceItem = {
74
+ id: string;
75
+ label: string;
76
+ type: 'select' | 'multi-select' | 'toggle' | 'slider';
77
+ options?: OnboardingOption[];
78
+ min?: number;
79
+ max?: number;
80
+ defaultValue?: string | number | string[] | boolean;
81
+ };
82
+
83
+ export type PreferencesScreenConfig = BaseScreen<
84
+ 'preferences',
85
+ Record<string, string | number | string[] | boolean>
86
+ > & {
87
+ title: string;
88
+ preferences: ReadonlyArray<PreferenceItem>;
89
+ onChange: (values: Record<string, string | number | string[] | boolean>) => void;
90
+ };
91
+
92
+ export type PermissionType = 'camera' | 'microphone' | 'location' | 'notifications' | 'photos' | 'contacts';
93
+
94
+ export type PermissionPrimingScreenConfig = BaseScreen<'permission-priming', PermissionStatus> & {
95
+ permission: PermissionType;
96
+ description: string;
97
+ benefits: ReadonlyArray<string>;
98
+ iconName?: string;
99
+ onRequest: () => Promise<PermissionStatus>;
100
+ };
101
+
102
+ export type PermissionStatus = 'granted' | 'denied' | 'prompt' | 'unsupported';
103
+
104
+ export type ProcessingScreenConfig = BaseScreen<'processing'> & {
105
+ title: string;
106
+ steps: ReadonlyArray<string>;
107
+ durationMs?: number;
108
+ onComplete: () => void;
109
+ };
110
+
111
+ export type DemoConfig<TProps = Record<string, unknown>, TResult = unknown> = {
112
+ component: React.ComponentType<TProps>;
113
+ props?: TProps;
114
+ onSave: (result: TResult) => void;
115
+ durationMs?: number;
116
+ };
117
+
118
+ export type AppDemoScreenConfig = BaseScreen<'app-demo', unknown> & {
119
+ demo: DemoConfig;
120
+ };
121
+
122
+ export type ValueDeliveryScreenConfig<TResult = unknown> = BaseScreen<
123
+ 'value-delivery',
124
+ TResult
125
+ > & {
126
+ title: string;
127
+ result: TResult;
128
+ shareConfig?: {
129
+ text: string;
130
+ url?: string;
131
+ };
132
+ };
133
+
134
+ export type SignInMethod = 'email' | 'google' | 'apple' | 'github';
135
+
136
+ export type AccountGateScreenConfig = BaseScreen<'account-gate', { signedIn: boolean; method?: SignInMethod }> & {
137
+ optional: boolean;
138
+ signInMethod?: SignInMethod;
139
+ skipText?: string;
140
+ onSignIn: (method: SignInMethod) => Promise<{ signedIn: boolean; method?: SignInMethod }>;
141
+ };
142
+
143
+ export type PaywallFeature = {
144
+ iconName: string; text: string;
145
+ highlighted?: boolean;
146
+ };
147
+
148
+ export type PaywallScreenConfig = BaseScreen<
149
+ 'paywall',
150
+ { accepted: boolean; trialStarted: boolean }
151
+ > & {
152
+ subtitle: string;
153
+ price: string;
154
+ trialDays: number;
155
+ trialPrice?: string;
156
+ features: ReadonlyArray<PaywallFeature>;
157
+ socialProof?: {
158
+ text: string;
159
+ count: number;
160
+ };
161
+ cta: string;
162
+ secondaryCta?: string;
163
+ onSecondaryCta?: () => void;
164
+ onAccept: () => Promise<{ accepted: boolean; trialStarted: boolean }>;
165
+ };
166
+
167
+ export type OnboardingScreen =
168
+ | WelcomeScreenConfig
169
+ | GoalQuestionScreenConfig
170
+ | PainPointsScreenConfig
171
+ | SocialProofScreenConfig
172
+ | TinderCardsScreenConfig
173
+ | PersonalizedSolutionScreenConfig
174
+ | ComparisonScreenConfig
175
+ | PreferencesScreenConfig
176
+ | PermissionPrimingScreenConfig
177
+ | ProcessingScreenConfig
178
+ | AppDemoScreenConfig
179
+ | ValueDeliveryScreenConfig
180
+ | AccountGateScreenConfig
181
+ | PaywallScreenConfig;
182
+
183
+ export type OnboardingScreenType = OnboardingScreen['type'];
184
+
185
+ export type { ReactNode };