Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | /** * useOnboarding - Hook for detecting and managing first-time user onboarding * * Determines if user needs to go through the onboarding wizard. * Stores completion status in localStorage. */ import { useState, useEffect, useCallback } from 'react'; const ONBOARDING_COMPLETED_KEY = 'zclaw-onboarding-completed'; const USER_PROFILE_KEY = 'zclaw-user-profile'; export interface UserProfile { userName: string; userRole?: string; completedAt: string; } export interface OnboardingState { isNeeded: boolean; isLoading: boolean; userProfile: UserProfile | null; markCompleted: (profile: Omit<UserProfile, 'completedAt'>) => void; resetOnboarding: () => void; } /** * Hook to manage first-time user onboarding * * Usage: * ```tsx * const { isNeeded, isLoading, markCompleted } = useOnboarding(); * * if (isNeeded) { * return <OnboardingWizard onComplete={markCompleted} />; * } * ``` */ export function useOnboarding(): OnboardingState { const [isNeeded, setIsNeeded] = useState(false); const [isLoading, setIsLoading] = useState(true); const [userProfile, setUserProfile] = useState<UserProfile | null>(null); // Check onboarding status on mount useEffect(() => { try { const completed = localStorage.getItem(ONBOARDING_COMPLETED_KEY); const profileStr = localStorage.getItem(USER_PROFILE_KEY); if (completed === 'true' && profileStr) { const profile = JSON.parse(profileStr) as UserProfile; setUserProfile(profile); setIsNeeded(false); } else { // No onboarding record - first time user setIsNeeded(true); } } catch (err) { console.warn('[useOnboarding] Failed to check onboarding status:', err); setIsNeeded(true); } finally { setIsLoading(false); } }, []); // Mark onboarding as completed const markCompleted = useCallback((profile: Omit<UserProfile, 'completedAt'>) => { const fullProfile: UserProfile = { ...profile, completedAt: new Date().toISOString(), }; try { localStorage.setItem(ONBOARDING_COMPLETED_KEY, 'true'); localStorage.setItem(USER_PROFILE_KEY, JSON.stringify(fullProfile)); setUserProfile(fullProfile); setIsNeeded(false); console.log('[useOnboarding] Onboarding completed for user:', profile.userName); } catch (err) { console.error('[useOnboarding] Failed to save onboarding status:', err); } }, []); // Reset onboarding (for testing or user request) const resetOnboarding = useCallback(() => { try { localStorage.removeItem(ONBOARDING_COMPLETED_KEY); localStorage.removeItem(USER_PROFILE_KEY); setUserProfile(null); setIsNeeded(true); console.log('[useOnboarding] Onboarding reset'); } catch (err) { console.error('[useOnboarding] Failed to reset onboarding:', err); } }, []); return { isNeeded, isLoading, userProfile, markCompleted, resetOnboarding, }; } /** * Get stored user profile without hook (for use outside React components) */ export function getStoredUserProfile(): UserProfile | null { try { const profileStr = localStorage.getItem(USER_PROFILE_KEY); if (profileStr) { return JSON.parse(profileStr) as UserProfile; } } catch (err) { console.warn('[useOnboarding] Failed to get user profile:', err); } return null; } /** * Check if onboarding is completed (for use outside React components) */ export function isOnboardingCompleted(): boolean { return localStorage.getItem(ONBOARDING_COMPLETED_KEY) === 'true'; } export default useOnboarding; |