@flamingo-stack/openframe-frontend-core 0.0.679 → 0.0.680

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 (108) hide show
  1. package/dist/{chunk-OV6YSKMP.cjs → chunk-2UTUZGMF.cjs} +17 -8
  2. package/dist/chunk-2UTUZGMF.cjs.map +1 -0
  3. package/dist/{chunk-25Y3UR3R.cjs → chunk-3YWZNB2S.cjs} +3 -3
  4. package/dist/{chunk-25Y3UR3R.cjs.map → chunk-3YWZNB2S.cjs.map} +1 -1
  5. package/dist/{chunk-UKZAW4FE.js → chunk-4DA64VEW.js} +3 -3
  6. package/dist/{chunk-HA3SBMR2.cjs → chunk-4NUKFUPW.cjs} +7 -7
  7. package/dist/{chunk-HA3SBMR2.cjs.map → chunk-4NUKFUPW.cjs.map} +1 -1
  8. package/dist/{chunk-TKL5JKZS.cjs → chunk-757VTCIH.cjs} +6 -6
  9. package/dist/{chunk-TKL5JKZS.cjs.map → chunk-757VTCIH.cjs.map} +1 -1
  10. package/dist/{chunk-HLMMG6Q7.js → chunk-75S3RKYX.js} +5 -5
  11. package/dist/{chunk-ICGGFQQU.js → chunk-7OZ5P7Z2.js} +4 -4
  12. package/dist/{chunk-CTF5IQQZ.js → chunk-BGHQW5D5.js} +17 -8
  13. package/dist/chunk-BGHQW5D5.js.map +1 -0
  14. package/dist/{chunk-CDMZOXS7.js → chunk-C5RRTWYW.js} +19 -13
  15. package/dist/{chunk-CDMZOXS7.js.map → chunk-C5RRTWYW.js.map} +1 -1
  16. package/dist/{chunk-EEL4RGHX.js → chunk-D5QSGV3I.js} +2 -2
  17. package/dist/{chunk-6LN7ZNHT.js → chunk-DIWJ6NBT.js} +2 -2
  18. package/dist/{chunk-IPZCEAAH.js → chunk-EWZPMPQP.js} +2 -2
  19. package/dist/{chunk-NQCID4VF.js → chunk-F5N2P2GD.js} +6 -6
  20. package/dist/{chunk-CY3XHQM3.cjs → chunk-F7EZBJTL.cjs} +3 -3
  21. package/dist/{chunk-CY3XHQM3.cjs.map → chunk-F7EZBJTL.cjs.map} +1 -1
  22. package/dist/{chunk-QU7SDFKO.cjs → chunk-FCGZIXI5.cjs} +55 -55
  23. package/dist/{chunk-QU7SDFKO.cjs.map → chunk-FCGZIXI5.cjs.map} +1 -1
  24. package/dist/{chunk-UAPSU6YW.cjs → chunk-FRXIDN6T.cjs} +3 -3
  25. package/dist/{chunk-UAPSU6YW.cjs.map → chunk-FRXIDN6T.cjs.map} +1 -1
  26. package/dist/{chunk-XSSYG7KU.js → chunk-JV7SNSYT.js} +5 -5
  27. package/dist/{chunk-HTNUTEXN.cjs → chunk-KQXKQ4PX.cjs} +17 -17
  28. package/dist/{chunk-HTNUTEXN.cjs.map → chunk-KQXKQ4PX.cjs.map} +1 -1
  29. package/dist/{chunk-3LW6AJMJ.cjs → chunk-KSDOCJ7B.cjs} +45 -45
  30. package/dist/{chunk-3LW6AJMJ.cjs.map → chunk-KSDOCJ7B.cjs.map} +1 -1
  31. package/dist/{chunk-KNPUHPIF.cjs → chunk-QFGPSBAX.cjs} +33 -33
  32. package/dist/{chunk-KNPUHPIF.cjs.map → chunk-QFGPSBAX.cjs.map} +1 -1
  33. package/dist/{chunk-ZB34GLOZ.cjs → chunk-RLUXVMSD.cjs} +40 -40
  34. package/dist/{chunk-ZB34GLOZ.cjs.map → chunk-RLUXVMSD.cjs.map} +1 -1
  35. package/dist/{chunk-GZIYE35W.cjs → chunk-RSDTLURW.cjs} +30 -24
  36. package/dist/chunk-RSDTLURW.cjs.map +1 -0
  37. package/dist/{chunk-RJYYGWJL.cjs → chunk-SJC2XUTE.cjs} +21 -21
  38. package/dist/{chunk-RJYYGWJL.cjs.map → chunk-SJC2XUTE.cjs.map} +1 -1
  39. package/dist/{chunk-L4NLXAET.js → chunk-SPI7DCD3.js} +2 -2
  40. package/dist/{chunk-4ZKZSWX4.js → chunk-ULOUTQBX.js} +3 -3
  41. package/dist/{chunk-2YMFBKYR.js → chunk-V4L2MYXY.js} +2 -2
  42. package/dist/{chunk-CGYFCEHM.cjs → chunk-VWYK6TSE.cjs} +10 -10
  43. package/dist/{chunk-CGYFCEHM.cjs.map → chunk-VWYK6TSE.cjs.map} +1 -1
  44. package/dist/{chunk-ZRN244JV.js → chunk-WTNFYZB6.js} +2 -2
  45. package/dist/{chunk-FUBOKAYO.js → chunk-YC7DIARJ.js} +4 -4
  46. package/dist/{chunk-7WGUMITJ.js → chunk-YDJBSKLB.js} +3 -3
  47. package/dist/{chunk-MGSWL3L3.cjs → chunk-YFU7BULM.cjs} +23 -23
  48. package/dist/{chunk-MGSWL3L3.cjs.map → chunk-YFU7BULM.cjs.map} +1 -1
  49. package/dist/{chunk-H27EHTLX.cjs → chunk-ZFQ3ZHMR.cjs} +3 -3
  50. package/dist/{chunk-H27EHTLX.cjs.map → chunk-ZFQ3ZHMR.cjs.map} +1 -1
  51. package/dist/components/case-studies/index.cjs +13 -13
  52. package/dist/components/case-studies/index.js +3 -3
  53. package/dist/components/contact/index.cjs +4 -4
  54. package/dist/components/contact/index.js +3 -3
  55. package/dist/components/docs/index.cjs +7 -7
  56. package/dist/components/docs/index.js +6 -6
  57. package/dist/components/embeds/index.cjs +4 -4
  58. package/dist/components/embeds/index.js +3 -3
  59. package/dist/components/faq/index.cjs +4 -4
  60. package/dist/components/faq/index.js +3 -3
  61. package/dist/components/features/index.cjs +3 -3
  62. package/dist/components/features/index.js +2 -2
  63. package/dist/components/help-center-pages/index.cjs +52 -52
  64. package/dist/components/help-center-pages/index.js +14 -14
  65. package/dist/components/index.cjs +33 -33
  66. package/dist/components/index.js +13 -13
  67. package/dist/components/meeting-scheduler/index.cjs +28 -28
  68. package/dist/components/meeting-scheduler/index.js +2 -2
  69. package/dist/components/navigation/index.cjs +6 -6
  70. package/dist/components/navigation/index.js +5 -5
  71. package/dist/components/onboarding-guides/index.cjs +6 -6
  72. package/dist/components/onboarding-guides/index.js +5 -5
  73. package/dist/components/shared/onboarding/onboarding-walkthrough.d.ts +9 -1
  74. package/dist/components/shared/onboarding/onboarding-walkthrough.d.ts.map +1 -1
  75. package/dist/components/tickets/index.cjs +7 -7
  76. package/dist/components/tickets/index.js +6 -6
  77. package/dist/components/ui/index.cjs +3 -3
  78. package/dist/components/ui/index.js +2 -2
  79. package/dist/hooks/index.cjs +2 -2
  80. package/dist/hooks/index.js +1 -1
  81. package/dist/hooks/ui/use-onboarding-state.d.ts +1 -0
  82. package/dist/hooks/ui/use-onboarding-state.d.ts.map +1 -1
  83. package/dist/index.cjs +7 -7
  84. package/dist/index.js +6 -6
  85. package/dist/utils/onboarding-storage.d.ts +5 -0
  86. package/dist/utils/onboarding-storage.d.ts.map +1 -1
  87. package/package.json +1 -1
  88. package/src/components/shared/onboarding/onboarding-walkthrough.tsx +26 -10
  89. package/src/hooks/ui/use-onboarding-state.ts +8 -6
  90. package/src/utils/__tests__/onboarding-storage.test.ts +42 -0
  91. package/src/utils/onboarding-storage.ts +15 -1
  92. package/dist/chunk-CTF5IQQZ.js.map +0 -1
  93. package/dist/chunk-GZIYE35W.cjs.map +0 -1
  94. package/dist/chunk-OV6YSKMP.cjs.map +0 -1
  95. /package/dist/{chunk-UKZAW4FE.js.map → chunk-4DA64VEW.js.map} +0 -0
  96. /package/dist/{chunk-HLMMG6Q7.js.map → chunk-75S3RKYX.js.map} +0 -0
  97. /package/dist/{chunk-ICGGFQQU.js.map → chunk-7OZ5P7Z2.js.map} +0 -0
  98. /package/dist/{chunk-EEL4RGHX.js.map → chunk-D5QSGV3I.js.map} +0 -0
  99. /package/dist/{chunk-6LN7ZNHT.js.map → chunk-DIWJ6NBT.js.map} +0 -0
  100. /package/dist/{chunk-IPZCEAAH.js.map → chunk-EWZPMPQP.js.map} +0 -0
  101. /package/dist/{chunk-NQCID4VF.js.map → chunk-F5N2P2GD.js.map} +0 -0
  102. /package/dist/{chunk-XSSYG7KU.js.map → chunk-JV7SNSYT.js.map} +0 -0
  103. /package/dist/{chunk-L4NLXAET.js.map → chunk-SPI7DCD3.js.map} +0 -0
  104. /package/dist/{chunk-4ZKZSWX4.js.map → chunk-ULOUTQBX.js.map} +0 -0
  105. /package/dist/{chunk-2YMFBKYR.js.map → chunk-V4L2MYXY.js.map} +0 -0
  106. /package/dist/{chunk-ZRN244JV.js.map → chunk-WTNFYZB6.js.map} +0 -0
  107. /package/dist/{chunk-FUBOKAYO.js.map → chunk-YC7DIARJ.js.map} +0 -0
  108. /package/dist/{chunk-7WGUMITJ.js.map → chunk-YDJBSKLB.js.map} +0 -0
@@ -9,6 +9,14 @@ import { OnboardingStepCard } from './onboarding-step-card';
9
9
 
10
10
  export interface OnboardingWalkthroughProps {
11
11
  steps: OnboardingStepConfig[];
12
+ /** Heading above the steps. Defaults to "Get Started". */
13
+ title?: string;
14
+ /**
15
+ * What to render once the walkthrough is dismissed. Receives `restart`,
16
+ * which clears every completed/skipped step and shows the walkthrough
17
+ * again. Omitted: a dismissed walkthrough renders nothing.
18
+ */
19
+ renderDismissed?: (restart: () => void) => React.ReactNode;
12
20
  onDismiss?: () => void;
13
21
  storageKey?: string;
14
22
  className?: string;
@@ -26,6 +34,8 @@ export interface OnboardingWalkthroughProps {
26
34
 
27
35
  export function OnboardingWalkthrough({
28
36
  steps,
37
+ title = 'Get Started',
38
+ renderDismissed,
29
39
  onDismiss,
30
40
  storageKey = 'openframe-onboarding-state',
31
41
  className,
@@ -38,6 +48,7 @@ export function OnboardingWalkthrough({
38
48
  markComplete,
39
49
  markSkipped,
40
50
  dismissOnboarding,
51
+ resetOnboarding,
41
52
  isStepComplete,
42
53
  isStepSkipped,
43
54
  allStepsComplete,
@@ -63,8 +74,11 @@ export function OnboardingWalkthrough({
63
74
  // Skip if no completion status
64
75
  if (!completionStatus) return;
65
76
 
66
- // Create a stable key from completion status to detect actual changes
67
- const statusKey = JSON.stringify(completionStatus);
77
+ // Create a stable key from completion status to detect actual changes.
78
+ // An empty state (first load, or after a restart) is keyed by when it was
79
+ // written, so a restart applies the same status again.
80
+ const isFreshState = state.completedSteps.length === 0 && state.skippedSteps.length === 0;
81
+ const statusKey = `${JSON.stringify(completionStatus)}|${isFreshState ? state.lastUpdated : ''}`;
68
82
 
69
83
  // Skip if we've already processed this exact status
70
84
  if (lastCompletionStatusRef.current === statusKey) return;
@@ -86,7 +100,6 @@ export function OnboardingWalkthrough({
86
100
  // Mark as in progress to prevent concurrent calls
87
101
  autoMarkingInProgressRef.current = true;
88
102
 
89
- console.log('📊 Auto-marking steps as complete from hook data:', completedStepIds);
90
103
  markMultipleComplete(completedStepIds);
91
104
 
92
105
  // Update the last processed status key
@@ -101,14 +114,20 @@ export function OnboardingWalkthrough({
101
114
  // Even if no steps to mark, update the status key to prevent re-processing
102
115
  lastCompletionStatusRef.current = statusKey;
103
116
  }
104
- }, [completionStatus, isLoadingCompletion, state.completedSteps, markMultipleComplete]);
117
+ }, [
118
+ completionStatus,
119
+ isLoadingCompletion,
120
+ state.completedSteps,
121
+ state.skippedSteps,
122
+ state.lastUpdated,
123
+ markMultipleComplete,
124
+ ]);
105
125
 
106
126
  // useCallback must be called unconditionally (before any early returns)
107
127
  const handleStepAction = useCallback(
108
128
  async (step: OnboardingStepConfig) => {
109
129
  // Prevent duplicate action handling for the same step
110
130
  if (actionInProgressRef.current.has(step.id)) {
111
- console.log(`⏳ Action already in progress for "${step.id}", skipping`);
112
131
  return;
113
132
  }
114
133
 
@@ -120,13 +139,11 @@ export function OnboardingWalkthrough({
120
139
 
121
140
  // Skip if already completed (could have been auto-marked while action was running)
122
141
  if (state.completedSteps.includes(step.id)) {
123
- console.log(`✓ Step "${step.id}" already completed, skipping mark`);
124
142
  return;
125
143
  }
126
144
 
127
145
  // Auto-mark Knowledge Base as complete (no verification needed)
128
146
  if (step.id === 'knowledge-base') {
129
- console.log(`🎯 Auto-completing Knowledge Base`);
130
147
  markComplete(step.id);
131
148
  return;
132
149
  }
@@ -135,7 +152,6 @@ export function OnboardingWalkthrough({
135
152
  if (step.checkComplete) {
136
153
  try {
137
154
  const isComplete = await step.checkComplete();
138
- console.log(`🔍 Post-action check for "${step.id}": isComplete=${isComplete}`);
139
155
 
140
156
  // Double-check it wasn't completed while we were checking
141
157
  if (isComplete && !state.completedSteps.includes(step.id)) {
@@ -166,7 +182,7 @@ export function OnboardingWalkthrough({
166
182
  // ALL hooks have been called above - now safe to do conditional returns
167
183
  // Don't render if dismissed
168
184
  if (state.dismissed) {
169
- return null;
185
+ return renderDismissed ? <>{renderDismissed(resetOnboarding)}</> : null;
170
186
  }
171
187
 
172
188
  const isAllComplete = allStepsComplete(steps);
@@ -175,7 +191,7 @@ export function OnboardingWalkthrough({
175
191
  <div className={cn('w-full space-y-4', className)}>
176
192
  {/* Header - responsive: stacks on mobile */}
177
193
  <div className="flex flex-col items-start justify-between gap-3 md:flex-row md:items-center md:gap-0">
178
- <h2 className={SECTION_HEADING_CLASS}>Get Started</h2>
194
+ <h2 className={SECTION_HEADING_CLASS}>{title}</h2>
179
195
 
180
196
  {isLoadingCompletion ? (
181
197
  <div className="h-[48px] w-full animate-pulse rounded-[6px] bg-ods-border md:w-[160px]" />
@@ -8,6 +8,7 @@ import {
8
8
  markStepSkipped as storageMarkSkipped,
9
9
  dismissOnboarding as storageDismiss,
10
10
  markMultipleComplete as storageMarkMultiple,
11
+ resetOnboardingState as storageReset,
11
12
  type OnboardingState,
12
13
  } from '../../utils/onboarding-storage';
13
14
  import { readLocalStorageUpdateDetail } from '../../utils/storage-event';
@@ -42,7 +43,6 @@ export function useOnboardingState(storageKey: string = 'openframe-onboarding-st
42
43
  const newState = loadOnboardingState(storageKey);
43
44
  setState(newState);
44
45
  forceUpdate(prev => prev + 1);
45
- console.log('🔄 State updated from storage event:', newState);
46
46
  };
47
47
 
48
48
  window.addEventListener('localStorageUpdate', handleStorageUpdate);
@@ -53,7 +53,6 @@ export function useOnboardingState(storageKey: string = 'openframe-onboarding-st
53
53
 
54
54
  const markComplete = useCallback(
55
55
  (stepId: string) => {
56
- console.log(`🎯 markComplete called for: "${stepId}"`);
57
56
  const newState = storageMarkComplete(storageKey, stepId);
58
57
  setState(newState);
59
58
  forceUpdate(prev => prev + 1);
@@ -63,7 +62,6 @@ export function useOnboardingState(storageKey: string = 'openframe-onboarding-st
63
62
 
64
63
  const markSkipped = useCallback(
65
64
  (stepId: string) => {
66
- console.log(`⏭️ markSkipped called for: "${stepId}"`);
67
65
  const newState = storageMarkSkipped(storageKey, stepId);
68
66
  setState(newState);
69
67
  forceUpdate(prev => prev + 1);
@@ -72,19 +70,22 @@ export function useOnboardingState(storageKey: string = 'openframe-onboarding-st
72
70
  );
73
71
 
74
72
  const dismissOnboarding = useCallback(() => {
75
- console.log(`🚫 dismissOnboarding called`);
76
73
  const newState = storageDismiss(storageKey);
77
74
  setState(newState);
78
75
  forceUpdate(prev => prev + 1);
79
76
  }, [storageKey]);
80
77
 
78
+ const resetOnboarding = useCallback(() => {
79
+ const newState = storageReset(storageKey);
80
+ setState(newState);
81
+ forceUpdate(prev => prev + 1);
82
+ }, [storageKey]);
83
+
81
84
  const markMultipleComplete = useCallback(
82
85
  (stepIds: string[]) => {
83
- console.log(`🎯 markMultipleComplete called for:`, stepIds);
84
86
  const newState = storageMarkMultiple(storageKey, stepIds);
85
87
  setState(newState);
86
88
  forceUpdate(prev => prev + 1);
87
- console.log(`📝 State after batch:`, newState);
88
89
  },
89
90
  [storageKey],
90
91
  );
@@ -115,6 +116,7 @@ export function useOnboardingState(storageKey: string = 'openframe-onboarding-st
115
116
  markComplete,
116
117
  markSkipped,
117
118
  dismissOnboarding,
119
+ resetOnboarding,
118
120
  isStepComplete,
119
121
  isStepSkipped,
120
122
  allStepsComplete,
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The reset that brings a dismissed walkthrough back, round-tripped through
3
+ * real localStorage: reading the state back is the only proof the write stuck.
4
+ */
5
+ import { afterEach, describe, expect, it } from 'vitest';
6
+ import {
7
+ dismissOnboarding,
8
+ loadOnboardingState,
9
+ markStepComplete,
10
+ markStepSkipped,
11
+ resetOnboardingState,
12
+ } from '../onboarding-storage';
13
+
14
+ const KEY = 'onboarding-storage-test';
15
+
16
+ afterEach(() => localStorage.clear());
17
+
18
+ describe('resetOnboardingState', () => {
19
+ it('clears completed and skipped steps and undoes a dismissal', () => {
20
+ markStepComplete(KEY, 'install');
21
+ markStepSkipped(KEY, 'video');
22
+ dismissOnboarding(KEY);
23
+ expect(loadOnboardingState(KEY).dismissed).toBe(true);
24
+
25
+ const reset = resetOnboardingState(KEY);
26
+
27
+ expect(reset).toMatchObject({ completedSteps: [], skippedSteps: [], dismissed: false });
28
+ expect(loadOnboardingState(KEY)).toMatchObject({ completedSteps: [], skippedSteps: [], dismissed: false });
29
+ });
30
+
31
+ it('tells other hook instances through the localStorageUpdate event', () => {
32
+ const seen: string[] = [];
33
+ const listener = (event: Event) => seen.push((event as CustomEvent<{ key: string }>).detail.key);
34
+ window.addEventListener('localStorageUpdate', listener);
35
+ try {
36
+ resetOnboardingState(KEY);
37
+ } finally {
38
+ window.removeEventListener('localStorageUpdate', listener);
39
+ }
40
+ expect(seen).toEqual([KEY]);
41
+ });
42
+ });
@@ -26,7 +26,6 @@ export function saveOnboardingState(key: string, state: OnboardingState): void {
26
26
 
27
27
  try {
28
28
  localStorage.setItem(key, JSON.stringify(state));
29
- console.log('💾 Saved onboarding state:', state);
30
29
 
31
30
  // Dispatch custom event for cross-tab sync
32
31
  if (typeof window !== 'undefined') {
@@ -110,6 +109,21 @@ export function dismissOnboarding(key: string): OnboardingState {
110
109
  return newState;
111
110
  }
112
111
 
112
+ /**
113
+ * Reset the walkthrough to its initial state: no step completed or skipped,
114
+ * not dismissed. How a dismissed walkthrough is brought back.
115
+ */
116
+ export function resetOnboardingState(key: string): OnboardingState {
117
+ const newState: OnboardingState = {
118
+ completedSteps: [],
119
+ skippedSteps: [],
120
+ dismissed: false,
121
+ lastUpdated: new Date().toISOString(),
122
+ };
123
+ saveOnboardingState(key, newState);
124
+ return newState;
125
+ }
126
+
113
127
  /**
114
128
  * Check if a step is complete
115
129
  */